From ab46d04d983fe145fa490b43bb2fe685a1c08809 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Wed, 8 Jan 2025 11:05:31 -0500 Subject: [PATCH 1/7] DEVSVCS-1087: remove unused automation hardhat tests (#15847) * remove unused automation hardhat tests * freeze contracts * remove more tests * update --- .../action.yml | 4 + .../automation/AutomationGasAnalysis.test.ts | 258 - .../automation/AutomationRegistrar2_1.test.ts | 1022 --- .../automation/AutomationRegistry2_2.test.ts | 5962 ----------------- .../test/v0.8/automation/CronUpkeep.test.ts | 576 -- .../v0.8/automation/CronUpkeepFactory.test.ts | 107 - .../automation/ERC20BalanceMonitor.test.ts | 695 -- .../v0.8/automation/EthBalanceMonitor.test.ts | 663 -- .../IAutomationRegistryMaster2_2.test.ts | 117 - .../LinkAvailableBalanceMonitor.test.ts | 1077 --- .../automation/UpkeepBalanceMonitor.test.ts | 402 -- .../automation/UpkeepTranscoder3_0.test.ts | 576 -- .../automation/UpkeepTranscoder4_0.test.ts | 654 -- contracts/test/v0.8/automation/helpers.ts | 68 - 14 files changed, 4 insertions(+), 12177 deletions(-) delete mode 100644 contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts delete mode 100644 contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts delete mode 100644 contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts delete mode 100644 contracts/test/v0.8/automation/CronUpkeep.test.ts delete mode 100644 contracts/test/v0.8/automation/CronUpkeepFactory.test.ts delete mode 100644 contracts/test/v0.8/automation/ERC20BalanceMonitor.test.ts delete mode 100644 contracts/test/v0.8/automation/EthBalanceMonitor.test.ts delete mode 100644 contracts/test/v0.8/automation/IAutomationRegistryMaster2_2.test.ts delete mode 100644 contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts delete mode 100644 contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts delete mode 100644 contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts delete mode 100644 contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts diff --git a/.github/actions/detect-solidity-readonly-file-changes/action.yml b/.github/actions/detect-solidity-readonly-file-changes/action.yml index faca16d53f0..d0890a9f604 100644 --- a/.github/actions/detect-solidity-readonly-file-changes/action.yml +++ b/.github/actions/detect-solidity-readonly-file-changes/action.yml @@ -16,9 +16,13 @@ runs: filters: | read_only_sol: - 'contracts/src/v0.8/interfaces/**/*' + - 'contracts/src/v0.8/automation/interfaces/**/*' + - 'contracts/src/v0.8/automation/upkeeps/**/*' - 'contracts/src/v0.8/automation/v1_2/**/*' - 'contracts/src/v0.8/automation/v1_3/**/*' - 'contracts/src/v0.8/automation/v2_0/**/*' + - 'contracts/src/v0.8/automation/v2_1/**/*' + - 'contracts/src/v0.8/automation/v2_2/**/*' - name: Fail if read-only files have changed if: ${{ steps.changed_files.outputs.read_only_sol == 'true' }} diff --git a/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts b/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts deleted file mode 100644 index f393a5de1c2..00000000000 --- a/contracts/test/v0.8/automation/AutomationGasAnalysis.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -import { ethers } from 'hardhat' -import { BigNumber } from 'ethers' -import { expect, assert } from 'chai' -import { getUsers } from '../../test-helpers/setup' -import { randomAddress, toWei } from '../../test-helpers/helpers' -import { deployRegistry21 } from './helpers' - -// don't run these tests in CI -const describeMaybe = process.env.CI ? describe.skip : describe - -// registry settings -const f = 1 -const linkEth = BigNumber.from(300000000) -const gasWei = BigNumber.from(100) -const minUpkeepSpend = BigNumber.from('1000000000000000000') -const paymentPremiumPPB = BigNumber.from(250000000) -const flatFeeMicroLink = BigNumber.from(0) -const blockCountPerTurn = 20 -const checkGasLimit = BigNumber.from(20000000) -const fallbackGasPrice = BigNumber.from(200) -const fallbackLinkPrice = BigNumber.from(200000000) -const maxCheckDataSize = BigNumber.from(10000) -const maxPerformDataSize = BigNumber.from(10000) -const maxRevertDataSize = BigNumber.from(1000) -const maxPerformGas = BigNumber.from(5000000) -const stalenessSeconds = BigNumber.from(43820) -const gasCeilingMultiplier = BigNumber.from(1) -const signers = [ - randomAddress(), - randomAddress(), - randomAddress(), - randomAddress(), -] -const transmitters = [ - randomAddress(), - randomAddress(), - randomAddress(), - randomAddress(), -] -const transcoder = ethers.constants.AddressZero - -// registrar settings -const triggerType = 0 // conditional -const autoApproveType = 2 // auto-approve enabled -const autoApproveMaxAllowed = 100 // auto-approve enabled - -// upkeep settings -const name = 'test upkeep' -const encryptedEmail = '0xabcd1234' -const gasLimit = 100_000 -const checkData = '0xdeadbeef' -const amount = toWei('5') -const source = 5 -const triggerConfig = '0x' -const offchainConfig = '0x' - -describeMaybe('Automation Gas Analysis', () => { - it('Compares gas usage amongst registries / registrars', async () => { - assert( - Boolean(process.env.REPORT_GAS), - 'this test must be run with REPORT_GAS=true', - ) - - const personas = (await getUsers()).personas - const owner = personas.Default - const ownerAddress = await owner.getAddress() - - // factories - const getFact = ethers.getContractFactory - const linkTokenFactory = await getFact('LinkToken') - const mockV3AggregatorFactory = await getFact( - 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator', - ) - const upkeepMockFactory = await getFact('UpkeepMock') - const registry12Factory = await getFact('KeeperRegistry1_2') - const registrar12Factory = await getFact('KeeperRegistrar') - const registry20Factory = await getFact('KeeperRegistry2_0') - const registryLogic20Factory = await getFact('KeeperRegistryLogic2_0') - const registrar20Factory = await getFact('KeeperRegistrar2_0') - const registrar21Factory = await getFact('AutomationRegistrar2_1') - const forwarderLogicFactory = await getFact('AutomationForwarderLogic') - - // deploy dependancy contracts - const linkToken = await linkTokenFactory.connect(owner).deploy() - const gasPriceFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(0, gasWei) - const linkEthFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(9, linkEth) - const upkeep = await upkeepMockFactory.connect(owner).deploy() - - // deploy v1.2 - const registrar12 = await registrar12Factory.connect(owner).deploy( - linkToken.address, - autoApproveType, - autoApproveMaxAllowed, - ethers.constants.AddressZero, // set later - minUpkeepSpend, - ) - const registry12 = await registry12Factory - .connect(owner) - .deploy(linkToken.address, linkEthFeed.address, gasPriceFeed.address, { - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder, - registrar: registrar12.address, - }) - await registrar12.setRegistrationConfig( - autoApproveType, - autoApproveMaxAllowed, - registry12.address, - minUpkeepSpend, - ) - - // deploy v2.0 - const registryLogic20 = await registryLogic20Factory - .connect(owner) - .deploy(0, linkToken.address, linkEthFeed.address, gasPriceFeed.address) - const registry20 = await registry20Factory - .connect(owner) - .deploy(registryLogic20.address) - const registrar20 = await registrar20Factory - .connect(owner) - .deploy( - linkToken.address, - autoApproveType, - autoApproveMaxAllowed, - registry20.address, - minUpkeepSpend, - ) - const config20 = { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder, - registrar: registrar20.address, - } - const onchainConfig20 = ethers.utils.defaultAbiCoder.encode( - [ - '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)', - ], - [config20], - ) - await registry20 - .connect(owner) - .setConfig(signers, transmitters, f, onchainConfig20, 1, '0x') - - // deploy v2.1 - const forwarderLogic = await forwarderLogicFactory.connect(owner).deploy() - const registry21 = await deployRegistry21( - owner, - 0, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - forwarderLogic.address, - ) - const registrar21 = await registrar21Factory - .connect(owner) - .deploy(linkToken.address, registry21.address, minUpkeepSpend, [ - { - triggerType, - autoApproveType, - autoApproveMaxAllowed, - }, - ]) - const onchainConfig21 = { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder, - registrars: [registrar21.address], - upkeepPrivilegeManager: randomAddress(), - } - await registry21 - .connect(owner) - .setConfigTypeSafe(signers, transmitters, f, onchainConfig21, 1, '0x') - - // approve LINK - await linkToken.connect(owner).approve(registrar20.address, amount) - await linkToken.connect(owner).approve(registrar21.address, amount) - - const abiEncodedBytes = registrar12.interface.encodeFunctionData( - 'register', - [ - name, - encryptedEmail, - upkeep.address, - gasLimit, - ownerAddress, - checkData, - amount, - source, - ownerAddress, - ], - ) - - let tx = await linkToken - .connect(owner) - .transferAndCall(registrar12.address, amount, abiEncodedBytes) - await expect(tx).to.emit(registry12, 'UpkeepRegistered') - - tx = await registrar20.connect(owner).registerUpkeep({ - name, - encryptedEmail, - upkeepContract: upkeep.address, - gasLimit, - adminAddress: ownerAddress, - checkData, - amount, - offchainConfig, - }) - await expect(tx).to.emit(registry20, 'UpkeepRegistered') - - tx = await registrar21.connect(owner).registerUpkeep({ - name, - encryptedEmail, - upkeepContract: upkeep.address, - gasLimit, - adminAddress: ownerAddress, - triggerType, - checkData, - amount, - triggerConfig, - offchainConfig, - }) - await expect(tx).to.emit(registry21, 'UpkeepRegistered') - }) -}) diff --git a/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts b/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts deleted file mode 100644 index 6d3d591acb0..00000000000 --- a/contracts/test/v0.8/automation/AutomationRegistrar2_1.test.ts +++ /dev/null @@ -1,1022 +0,0 @@ -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { AutomationRegistrar2_1__factory as AutomationRegistrarFactory } from '../../../typechain/factories/AutomationRegistrar2_1__factory' - -////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// - -/*********************************** REGISTRAR v2.1 IS FROZEN ************************************/ - -// As 2.1 is still actively being deployed, we keep the tests below. - -describe('AutomationRegistrar2_1 - Frozen [ @skip-coverage ]', () => { - it('has not changed', () => { - assert.equal( - ethers.utils.id(AutomationRegistrarFactory.bytecode), - '0x9633058bd81e8479f88baaee9bda533406295c80ccbc43d4509701001bbea6e3', - 'KeeperRegistry bytecode has changed', - ) - }) -}) - -////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// // copied from KeeperRegistryBase2_1.sol -// enum Trigger { -// CONDITION, -// LOG, -// } -// -// let linkTokenFactory: LinkTokenFactory -// let mockV3AggregatorFactory: MockV3AggregatorFactory -// let upkeepMockFactory: UpkeepMockFactory -// -// let personas: Personas -// -// before(async () => { -// personas = (await getUsers()).personas -// -// linkTokenFactory = await ethers.getContractFactory( -// 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', -// ) -// mockV3AggregatorFactory = (await ethers.getContractFactory( -// 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator', -// )) as unknown as MockV3AggregatorFactory -// upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') -// }) -// -// const errorMsgs = { -// onlyOwner: 'revert Only callable by owner', -// onlyAdmin: 'OnlyAdminOrOwner()', -// hashPayload: 'HashMismatch()', -// requestNotFound: 'RequestNotFound()', -// } -// -// describe('AutomationRegistrar2_1', () => { -// const upkeepName = 'SampleUpkeep' -// -// const linkEth = BigNumber.from(300000000) -// const gasWei = BigNumber.from(100) -// const performGas = BigNumber.from(100000) -// const paymentPremiumPPB = BigNumber.from(250000000) -// const flatFeeMicroLink = BigNumber.from(0) -// const maxAllowedAutoApprove = 5 -// const trigger = '0xdeadbeef' -// const offchainConfig = '0x01234567' -// -// const emptyBytes = '0x00' -// const stalenessSeconds = BigNumber.from(43820) -// const gasCeilingMultiplier = BigNumber.from(1) -// const checkGasLimit = BigNumber.from(20000000) -// const fallbackGasPrice = BigNumber.from(200) -// const fallbackLinkPrice = BigNumber.from(200000000) -// const maxCheckDataSize = BigNumber.from(10000) -// const maxPerformDataSize = BigNumber.from(10000) -// const maxRevertDataSize = BigNumber.from(1000) -// const maxPerformGas = BigNumber.from(5000000) -// const minUpkeepSpend = BigNumber.from('1000000000000000000') -// const amount = BigNumber.from('5000000000000000000') -// const amount1 = BigNumber.from('6000000000000000000') -// const transcoder = ethers.constants.AddressZero -// const upkeepManager = ethers.Wallet.createRandom().address -// -// // Enum values are not auto exported in ABI so have to manually declare -// const autoApproveType_DISABLED = 0 -// const autoApproveType_ENABLED_SENDER_ALLOWLIST = 1 -// const autoApproveType_ENABLED_ALL = 2 -// -// let owner: Signer -// let admin: Signer -// let someAddress: Signer -// let registrarOwner: Signer -// let stranger: Signer -// let requestSender: Signer -// -// let linkToken: LinkToken -// let linkEthFeed: MockV3Aggregator -// let gasPriceFeed: MockV3Aggregator -// let mock: UpkeepMock -// let registry: IKeeperRegistry -// let registrar: Registrar -// -// beforeEach(async () => { -// owner = personas.Default -// admin = personas.Neil -// someAddress = personas.Ned -// registrarOwner = personas.Nelly -// stranger = personas.Nancy -// requestSender = personas.Norbert -// -// linkToken = await linkTokenFactory.connect(owner).deploy() -// gasPriceFeed = await mockV3AggregatorFactory -// .connect(owner) -// .deploy(0, gasWei) -// linkEthFeed = await mockV3AggregatorFactory -// .connect(owner) -// .deploy(9, linkEth) -// -// registry = await deployRegistry21( -// owner, -// 0, -// linkToken.address, -// linkEthFeed.address, -// gasPriceFeed.address, -// ) -// -// mock = await upkeepMockFactory.deploy() -// -// const registrarFactory = await ethers.getContractFactory( -// 'AutomationRegistrar2_1', -// ) -// registrar = await registrarFactory -// .connect(registrarOwner) -// .deploy(linkToken.address, registry.address, minUpkeepSpend, [ -// { -// triggerType: Trigger.CONDITION, -// autoApproveType: autoApproveType_DISABLED, -// autoApproveMaxAllowed: 0, -// }, -// { -// triggerType: Trigger.LOG, -// autoApproveType: autoApproveType_DISABLED, -// autoApproveMaxAllowed: 0, -// }, -// ]) -// -// await linkToken -// .connect(owner) -// .transfer(await requestSender.getAddress(), toWei('1000')) -// -// const keepers = [ -// await personas.Carol.getAddress(), -// await personas.Nancy.getAddress(), -// await personas.Ned.getAddress(), -// await personas.Neil.getAddress(), -// ] -// const onchainConfig = { -// paymentPremiumPPB, -// flatFeeMicroLink, -// checkGasLimit, -// stalenessSeconds, -// gasCeilingMultiplier, -// minUpkeepSpend, -// maxCheckDataSize, -// maxPerformDataSize, -// maxRevertDataSize, -// maxPerformGas, -// fallbackGasPrice, -// fallbackLinkPrice, -// transcoder, -// registrars: [registrar.address], -// upkeepPrivilegeManager: upkeepManager, -// } -// await registry -// .connect(owner) -// .setConfigTypeSafe(keepers, keepers, 1, onchainConfig, 1, '0x') -// }) -// -// describe('#typeAndVersion', () => { -// it('uses the correct type and version', async () => { -// const typeAndVersion = await registrar.typeAndVersion() -// assert.equal(typeAndVersion, 'AutomationRegistrar 2.1.0') -// }) -// }) -// -// describe('#register', () => { -// it('reverts if not called by the LINK token', async () => { -// await evmRevert( -// registrar -// .connect(someAddress) -// .register( -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ), -// 'OnlyLink()', -// ) -// }) -// -// it('reverts if the amount passed in data mismatches actual amount sent', async () => { -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_ENABLED_ALL, -// maxAllowedAutoApprove, -// ) -// -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount1, -// await requestSender.getAddress(), -// ], -// ) -// -// await evmRevert( -// linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes), -// 'AmountMismatch()', -// ) -// }) -// -// it('reverts if the sender passed in data mismatches actual sender', async () => { -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await admin.getAddress(), // Should have been requestSender.getAddress() -// ], -// ) -// await evmRevert( -// linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes), -// 'SenderMismatch()', -// ) -// }) -// -// it('reverts if the admin address is 0x0000...', async () => { -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// '0x0000000000000000000000000000000000000000', -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ], -// ) -// -// await evmRevert( -// linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes), -// 'RegistrationRequestFailed()', -// ) -// }) -// -// it('Auto Approve ON - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => { -// //set auto approve ON with high threshold limits -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_ENABLED_ALL, -// maxAllowedAutoApprove, -// ) -// -// //register with auto approve ON -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ], -// ) -// const tx = await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// -// const [id] = await registry.getActiveUpkeepIDs(0, 1) -// -// //confirm if a new upkeep has been registered and the details are the same as the one just registered -// const newupkeep = await registry.getUpkeep(id) -// 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.performGas, performGas.toNumber()) -// assert.equal(newupkeep.offchainConfig, offchainConfig) -// -// 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.getState()).state.numUpkeeps -// -// //set auto approve OFF, threshold limits dont matter in this case -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_DISABLED, -// maxAllowedAutoApprove, -// ) -// -// //register with auto approve OFF -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ], -// ) -// const tx = await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// const receipt = await tx.wait() -// -// //get upkeep count after attempting registration -// const afterCount = (await registry.getState()).state.numUpkeeps -// //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 register an upkeep on KeeperRegistry beyond the max limit, emits only RegistrationRequested event after limit is hit', async () => { -// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 0) -// -// //set auto approve on, with max 1 allowed -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig(Trigger.CONDITION, autoApproveType_ENABLED_ALL, 1) -// -// //set auto approve on, with max 1 allowed -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 1) -// -// // register within threshold, new upkeep should be registered -// let abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ]) -// await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // 0 -> 1 -// -// // try registering another one, new upkeep should not be registered -// abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas.toNumber() + 1, // make unique hash -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ]) -// await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // Still 1 -// -// // register a second type of upkeep, different limit -// abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// Trigger.LOG, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ]) -// await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 2) // 1 -> 2 -// -// // Now set new max limit to 2. One more upkeep should get auto approved -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig(Trigger.CONDITION, autoApproveType_ENABLED_ALL, 2) -// -// abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas.toNumber() + 2, // make unique hash -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ]) -// await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 3) // 2 -> 3 -// -// // One more upkeep should not get registered -// abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas.toNumber() + 3, // make unique hash -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ]) -// await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 3) // Still 3 -// }) -// -// it('Auto Approve Sender Allowlist - sender in allowlist - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => { -// const senderAddress = await requestSender.getAddress() -// -// //set auto approve to ENABLED_SENDER_ALLOWLIST type with high threshold limits -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_ENABLED_SENDER_ALLOWLIST, -// maxAllowedAutoApprove, -// ) -// -// // Add sender to allowlist -// await registrar -// .connect(registrarOwner) -// .setAutoApproveAllowedSender(senderAddress, true) -// -// //register with auto approve ON -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ], -// ) -// const tx = await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// -// const [id] = await registry.getActiveUpkeepIDs(0, 1) -// -// //confirm if a new upkeep has been registered and the details are the same as the one just registered -// const newupkeep = await registry.getUpkeep(id) -// 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.performGas, performGas.toNumber()) -// -// await expect(tx).to.emit(registrar, 'RegistrationRequested') -// await expect(tx).to.emit(registrar, 'RegistrationApproved') -// }) -// -// it('Auto Approve Sender Allowlist - sender NOT in allowlist - does not registers an upkeep on KeeperRegistry, emits only RegistrationRequested event', async () => { -// const beforeCount = (await registry.getState()).state.numUpkeeps -// const senderAddress = await requestSender.getAddress() -// -// //set auto approve to ENABLED_SENDER_ALLOWLIST type with high threshold limits -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_ENABLED_SENDER_ALLOWLIST, -// maxAllowedAutoApprove, -// ) -// -// // Explicitly remove sender from allowlist -// await registrar -// .connect(registrarOwner) -// .setAutoApproveAllowedSender(senderAddress, false) -// -// //register. auto approve shouldn't happen -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ], -// ) -// const tx = await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// const receipt = await tx.wait() -// -// //get upkeep count after attempting registration -// const afterCount = (await registry.getState()).state.numUpkeeps -// //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])) -// }) -// }) -// -// describe('#registerUpkeep', () => { -// it('reverts with empty message if amount sent is not available in LINK allowance', async () => { -// await evmRevert( -// registrar.connect(someAddress).registerUpkeep({ -// name: upkeepName, -// upkeepContract: mock.address, -// gasLimit: performGas, -// adminAddress: await admin.getAddress(), -// triggerType: 0, -// checkData: emptyBytes, -// triggerConfig: trigger, -// offchainConfig: emptyBytes, -// amount, -// encryptedEmail: emptyBytes, -// }), -// '', -// ) -// }) -// -// it('reverts if the amount passed in data is less than configured minimum', async () => { -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_ENABLED_ALL, -// maxAllowedAutoApprove, -// ) -// -// // amt is one order of magnitude less than minUpkeepSpend -// const amt = BigNumber.from('100000000000000000') -// -// await evmRevert( -// registrar.connect(someAddress).registerUpkeep({ -// name: upkeepName, -// upkeepContract: mock.address, -// gasLimit: performGas, -// adminAddress: await admin.getAddress(), -// triggerType: 0, -// checkData: emptyBytes, -// triggerConfig: trigger, -// offchainConfig: emptyBytes, -// amount: amt, -// encryptedEmail: emptyBytes, -// }), -// 'InsufficientPayment()', -// ) -// }) -// -// it('Auto Approve ON - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => { -// //set auto approve ON with high threshold limits -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_ENABLED_ALL, -// maxAllowedAutoApprove, -// ) -// -// await linkToken.connect(requestSender).approve(registrar.address, amount) -// -// const tx = await registrar.connect(requestSender).registerUpkeep({ -// name: upkeepName, -// upkeepContract: mock.address, -// gasLimit: performGas, -// adminAddress: await admin.getAddress(), -// triggerType: 0, -// checkData: emptyBytes, -// triggerConfig: trigger, -// offchainConfig, -// amount, -// encryptedEmail: emptyBytes, -// }) -// assert.equal((await registry.getState()).state.numUpkeeps.toNumber(), 1) // 0 -> 1 -// -// //confirm if a new upkeep has been registered and the details are the same as the one just registered -// const [id] = await registry.getActiveUpkeepIDs(0, 1) -// const newupkeep = await registry.getUpkeep(id) -// 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.performGas, performGas.toNumber()) -// assert.equal(newupkeep.offchainConfig, offchainConfig) -// -// await expect(tx).to.emit(registrar, 'RegistrationRequested') -// await expect(tx).to.emit(registrar, 'RegistrationApproved') -// }) -// }) -// -// describe('#setAutoApproveAllowedSender', () => { -// it('reverts if not called by the owner', async () => { -// const tx = registrar -// .connect(stranger) -// .setAutoApproveAllowedSender(await admin.getAddress(), false) -// await evmRevert(tx, 'Only callable by owner') -// }) -// -// it('sets the allowed status correctly and emits log', async () => { -// const senderAddress = await stranger.getAddress() -// let tx = await registrar -// .connect(registrarOwner) -// .setAutoApproveAllowedSender(senderAddress, true) -// await expect(tx) -// .to.emit(registrar, 'AutoApproveAllowedSenderSet') -// .withArgs(senderAddress, true) -// -// let senderAllowedStatus = await registrar -// .connect(owner) -// .getAutoApproveAllowedSender(senderAddress) -// assert.isTrue(senderAllowedStatus) -// -// tx = await registrar -// .connect(registrarOwner) -// .setAutoApproveAllowedSender(senderAddress, false) -// await expect(tx) -// .to.emit(registrar, 'AutoApproveAllowedSenderSet') -// .withArgs(senderAddress, false) -// -// senderAllowedStatus = await registrar -// .connect(owner) -// .getAutoApproveAllowedSender(senderAddress) -// assert.isFalse(senderAllowedStatus) -// }) -// }) -// -// describe('#setTriggerConfig', () => { -// it('reverts if not called by the owner', async () => { -// const tx = registrar -// .connect(stranger) -// .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 100) -// await evmRevert(tx, 'Only callable by owner') -// }) -// -// it('changes the config', async () => { -// const tx = await registrar -// .connect(registrarOwner) -// .setTriggerConfig(Trigger.LOG, autoApproveType_ENABLED_ALL, 100) -// await registrar.getTriggerRegistrationDetails(Trigger.LOG) -// await expect(tx) -// .to.emit(registrar, 'TriggerConfigSet') -// .withArgs(Trigger.LOG, autoApproveType_ENABLED_ALL, 100) -// }) -// }) -// -// describe('#approve', () => { -// let hash: string -// -// beforeEach(async () => { -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_DISABLED, -// maxAllowedAutoApprove, -// ) -// -// //register with auto approve OFF -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ], -// ) -// -// const tx = await linkToken -// .connect(requestSender) -// .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, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// 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, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// 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, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// emptyBytes, -// hash, -// ) -// await evmRevert(tx, errorMsgs.hashPayload) -// tx = registrar -// .connect(registrarOwner) -// .approve( -// upkeepName, -// mock.address, -// 10000, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// emptyBytes, -// hash, -// ) -// await evmRevert(tx, errorMsgs.hashPayload) -// tx = registrar -// .connect(registrarOwner) -// .approve( -// upkeepName, -// mock.address, -// performGas, -// ethers.Wallet.createRandom().address, -// 0, -// emptyBytes, -// trigger, -// emptyBytes, -// hash, -// ) -// await evmRevert(tx, errorMsgs.hashPayload) -// tx = registrar -// .connect(registrarOwner) -// .approve( -// upkeepName, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// '0x1234', -// trigger, -// emptyBytes, -// hash, -// ) -// await evmRevert(tx, errorMsgs.hashPayload) -// }) -// -// it('approves an existing registration request', async () => { -// const tx = await registrar -// .connect(registrarOwner) -// .approve( -// upkeepName, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// 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, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// hash, -// ) -// const tx = registrar -// .connect(registrarOwner) -// .approve( -// upkeepName, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// hash, -// ) -// await evmRevert(tx, errorMsgs.requestNotFound) -// }) -// }) -// -// describe('#cancel', () => { -// let hash: string -// -// beforeEach(async () => { -// await registrar -// .connect(registrarOwner) -// .setTriggerConfig( -// Trigger.CONDITION, -// autoApproveType_DISABLED, -// maxAllowedAutoApprove, -// ) -// -// //register with auto approve OFF -// const abiEncodedBytes = registrar.interface.encodeFunctionData( -// 'register', -// [ -// upkeepName, -// emptyBytes, -// mock.address, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// offchainConfig, -// amount, -// await requestSender.getAddress(), -// ], -// ) -// const tx = await linkToken -// .connect(requestSender) -// .transferAndCall(registrar.address, amount, abiEncodedBytes) -// const receipt = await tx.wait() -// hash = receipt.logs[2].topics[1] -// // submit duplicate request (increase balance) -// await linkToken -// .connect(requestSender) -// .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, errorMsgs.requestNotFound) -// }) -// -// it('refunds the total request balance to the admin address if owner cancels', async () => { -// const before = await linkToken.balanceOf(await admin.getAddress()) -// const tx = await registrar.connect(registrarOwner).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('refunds the total request balance to the admin address if admin cancels', 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, -// performGas, -// await admin.getAddress(), -// 0, -// emptyBytes, -// trigger, -// emptyBytes, -// hash, -// ) -// await evmRevert(tx, errorMsgs.requestNotFound) -// }) -// }) -// }) diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts deleted file mode 100644 index 593ac08a5e7..00000000000 --- a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts +++ /dev/null @@ -1,5962 +0,0 @@ -import { ethers } from 'hardhat' -import { loadFixture } from '@nomicfoundation/hardhat-network-helpers' -import { assert, expect } from 'chai' -import { - BigNumber, - BigNumberish, - BytesLike, - Contract, - ContractFactory, - ContractReceipt, - ContractTransaction, - Signer, - Wallet, -} from 'ethers' -import { evmRevert, evmRevertCustomError } from '../../test-helpers/matchers' -import { getUsers, Personas } from '../../test-helpers/setup' -import { randomAddress, toWei } from '../../test-helpers/helpers' -import { StreamsLookupUpkeep__factory as StreamsLookupUpkeepFactory } from '../../../typechain/factories/StreamsLookupUpkeep__factory' -import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../../typechain/factories/MockV3Aggregator__factory' -import { UpkeepMock__factory as UpkeepMockFactory } from '../../../typechain/factories/UpkeepMock__factory' -import { UpkeepAutoFunder__factory as UpkeepAutoFunderFactory } from '../../../typechain/factories/UpkeepAutoFunder__factory' -import { MockArbGasInfo__factory as MockArbGasInfoFactory } from '../../../typechain/factories/MockArbGasInfo__factory' -import { MockOVMGasPriceOracle__factory as MockOVMGasPriceOracleFactory } from '../../../typechain/factories/MockOVMGasPriceOracle__factory' -import { ChainModuleBase__factory as ChainModuleBaseFactory } from '../../../typechain/factories/ChainModuleBase__factory' -import { ArbitrumModule__factory as ArbitrumModuleFactory } from '../../../typechain/factories/ArbitrumModule__factory' -import { OptimismModuleV2__factory as OptimismModuleV2Factory } from '../../../typechain/factories/OptimismModuleV2__factory' -import { ILogAutomation__factory as ILogAutomationactory } from '../../../typechain/factories/ILogAutomation__factory' -import { IAutomationForwarder__factory as IAutomationForwarderFactory } from '../../../typechain/factories/IAutomationForwarder__factory' -import { MockArbSys__factory as MockArbSysFactory } from '../../../typechain/factories/MockArbSys__factory' -import { AutomationCompatibleUtils } from '../../../typechain/AutomationCompatibleUtils' -import { MockArbGasInfo } from '../../../typechain/MockArbGasInfo' -import { MockOVMGasPriceOracle } from '../../../typechain/MockOVMGasPriceOracle' -import { StreamsLookupUpkeep } from '../../../typechain/StreamsLookupUpkeep' -import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator' -import { UpkeepMock } from '../../../typechain/UpkeepMock' -import { ChainModuleBase } from '../../../typechain/ChainModuleBase' -import { ArbitrumModule } from '../../../typechain/ArbitrumModule' -import { OptimismModuleV2 } from '../../../typechain/OptimismModuleV2' -import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder' -import { IChainModule, UpkeepAutoFunder } from '../../../typechain' -import { - CancelledUpkeepReportEvent, - IAutomationRegistryMaster as IAutomationRegistry, - ReorgedUpkeepReportEvent, - StaleUpkeepReportEvent, - UpkeepPerformedEvent, -} from '../../../typechain/IAutomationRegistryMaster' -import { - deployMockContract, - MockContract, -} from '@ethereum-waffle/mock-contract' -import { deployRegistry22 } from './helpers' - -const describeMaybe = process.env.SKIP_SLOW ? describe.skip : describe -const itMaybe = process.env.SKIP_SLOW ? it.skip : it - -// copied from AutomationRegistryInterface2_2.sol -enum UpkeepFailureReason { - NONE, - UPKEEP_CANCELLED, - UPKEEP_PAUSED, - TARGET_CHECK_REVERTED, - UPKEEP_NOT_NEEDED, - PERFORM_DATA_EXCEEDS_LIMIT, - INSUFFICIENT_BALANCE, - CHECK_CALLBACK_REVERTED, - REVERT_DATA_EXCEEDS_LIMIT, - REGISTRY_PAUSED, -} - -// copied from AutomationRegistryBase2_2.sol -enum Trigger { - CONDITION, - LOG, -} - -// un-exported types that must be extracted from the utils contract -type Report = Parameters[0] -type LogTrigger = Parameters[0] -type ConditionalTrigger = Parameters< - AutomationCompatibleUtils['_conditionalTrigger'] ->[0] -type Log = Parameters[0] - -// ----------------------------------------------------------------------------------------------- - -// These values should match the constants declared in registry -let registryConditionalOverhead: BigNumber -let registryLogOverhead: BigNumber -let registryPerSignerGasOverhead: BigNumber -let registryPerPerformByteGasOverhead: BigNumber -let registryTransmitCalldataFixedBytesOverhead: BigNumber -let registryTransmitCalldataPerSignerBytesOverhead: BigNumber -let cancellationDelay: number - -// This is the margin for gas that we test for. Gas charged should always be greater -// than total gas used in tx but should not increase beyond this margin -const gasCalculationMargin = BigNumber.from(5000) -// This is the margin for gas overhead estimation in checkUpkeep. The estimated gas -// overhead should be larger than actual gas overhead but should not increase beyond this margin -const gasEstimationMargin = BigNumber.from(5000) - -const linkEth = BigNumber.from(5000000000000000) // 1 Link = 0.005 Eth -const gasWei = BigNumber.from(1000000000) // 1 gwei -// ----------------------------------------------------------------------------------------------- -// test-wide configs for upkeeps -const linkDivisibility = BigNumber.from('1000000000000000000') -const performGas = BigNumber.from('1000000') -const paymentPremiumBase = BigNumber.from('1000000000') -const paymentPremiumPPB = BigNumber.from('250000000') -const flatFeeMicroLink = BigNumber.from(0) - -const randomBytes = '0x1234abcd' -const emptyBytes = '0x' -const emptyBytes32 = - '0x0000000000000000000000000000000000000000000000000000000000000000' - -const transmitGasOverhead = 1_000_000 -const checkGasOverhead = 500_000 - -const stalenessSeconds = BigNumber.from(43820) -const gasCeilingMultiplier = BigNumber.from(2) -const checkGasLimit = BigNumber.from(10000000) -const fallbackGasPrice = gasWei.mul(BigNumber.from('2')) -const fallbackLinkPrice = linkEth.div(BigNumber.from('2')) -const maxCheckDataSize = BigNumber.from(1000) -const maxPerformDataSize = BigNumber.from(1000) -const maxRevertDataSize = BigNumber.from(1000) -const maxPerformGas = BigNumber.from(5000000) -const minUpkeepSpend = BigNumber.from(0) -const f = 1 -const offchainVersion = 1 -const offchainBytes = '0x' -const zeroAddress = ethers.constants.AddressZero -const epochAndRound5_1 = - '0x0000000000000000000000000000000000000000000000000000000000000501' - -let logTriggerConfig: string - -// ----------------------------------------------------------------------------------------------- - -// Smart contract factories -let linkTokenFactory: ContractFactory -let mockArbGasInfoFactory: MockArbGasInfoFactory -let mockOVMGasPriceOracleFactory: MockOVMGasPriceOracleFactory -let mockV3AggregatorFactory: MockV3AggregatorFactory -let upkeepMockFactory: UpkeepMockFactory -let upkeepAutoFunderFactory: UpkeepAutoFunderFactory -let chainModuleBaseFactory: ChainModuleBaseFactory -let arbitrumModuleFactory: ArbitrumModuleFactory -let optimismModuleV2Factory: OptimismModuleV2Factory -let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory -let personas: Personas - -// contracts -let linkToken: Contract -let linkEthFeed: MockV3Aggregator -let gasPriceFeed: MockV3Aggregator -let registry: IAutomationRegistry // default registry, used for most tests -let arbRegistry: IAutomationRegistry // arbitrum registry -let opRegistry: IAutomationRegistry // optimism registry -let mgRegistry: IAutomationRegistry // "migrate registry" used in migration tests -let blankRegistry: IAutomationRegistry // used to test initial configurations -let mockArbGasInfo: MockArbGasInfo -let mockOVMGasPriceOracle: MockOVMGasPriceOracle -let mock: UpkeepMock -let autoFunderUpkeep: UpkeepAutoFunder -let ltUpkeep: MockContract -let transcoder: UpkeepTranscoder -let chainModuleBase: ChainModuleBase -let arbitrumModule: ArbitrumModule -let optimismModule: OptimismModuleV2 -let streamsLookupUpkeep: StreamsLookupUpkeep -let automationUtils: AutomationCompatibleUtils - -function now() { - return Math.floor(Date.now() / 1000) -} - -async function getUpkeepID(tx: ContractTransaction): Promise { - const receipt = await tx.wait() - for (const event of receipt.events || []) { - if ( - event.args && - event.eventSignature == 'UpkeepRegistered(uint256,uint32,address)' - ) { - return event.args[0] - } - } - throw new Error('could not find upkeep ID in tx event logs') -} - -const getTriggerType = (upkeepId: BigNumber): Trigger => { - const hexBytes = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) - const bytes = ethers.utils.arrayify(hexBytes) - for (let idx = 4; idx < 15; idx++) { - if (bytes[idx] != 0) { - return Trigger.CONDITION - } - } - return bytes[15] as Trigger -} - -const encodeBlockTrigger = (conditionalTrigger: ConditionalTrigger) => { - return ( - '0x' + - automationUtils.interface - .encodeFunctionData('_conditionalTrigger', [conditionalTrigger]) - .slice(10) - ) -} - -const encodeLogTrigger = (logTrigger: LogTrigger) => { - return ( - '0x' + - automationUtils.interface - .encodeFunctionData('_logTrigger', [logTrigger]) - .slice(10) - ) -} - -const encodeLog = (log: Log) => { - return ( - '0x' + automationUtils.interface.encodeFunctionData('_log', [log]).slice(10) - ) -} - -const encodeReport = (report: Report) => { - return ( - '0x' + - automationUtils.interface.encodeFunctionData('_report', [report]).slice(10) - ) -} - -type UpkeepData = { - Id: BigNumberish - performGas: BigNumberish - performData: BytesLike - trigger: BytesLike -} - -const makeReport = (upkeeps: UpkeepData[]) => { - const upkeepIds = upkeeps.map((u) => u.Id) - const performGases = upkeeps.map((u) => u.performGas) - const triggers = upkeeps.map((u) => u.trigger) - const performDatas = upkeeps.map((u) => u.performData) - return encodeReport({ - fastGasWei: gasWei, - linkNative: linkEth, - upkeepIds, - gasLimits: performGases, - triggers, - performDatas, - }) -} - -const makeLatestBlockReport = async (upkeepsIDs: BigNumberish[]) => { - const latestBlock = await ethers.provider.getBlock('latest') - const upkeeps: UpkeepData[] = [] - for (let i = 0; i < upkeepsIDs.length; i++) { - upkeeps.push({ - Id: upkeepsIDs[i], - performGas, - trigger: encodeBlockTrigger({ - blockNum: latestBlock.number, - blockHash: latestBlock.hash, - }), - performData: '0x', - }) - } - return makeReport(upkeeps) -} - -const signReport = ( - reportContext: string[], - report: any, - signers: Wallet[], -) => { - const reportDigest = ethers.utils.keccak256(report) - const packedArgs = ethers.utils.solidityPack( - ['bytes32', 'bytes32[3]'], - [reportDigest, reportContext], - ) - const packedDigest = ethers.utils.keccak256(packedArgs) - - const signatures = [] - for (const signer of signers) { - signatures.push(signer._signingKey().signDigest(packedDigest)) - } - const vs = signatures.map((i) => '0' + (i.v - 27).toString(16)).join('') - return { - vs: '0x' + vs.padEnd(64, '0'), - rs: signatures.map((i) => i.r), - ss: signatures.map((i) => i.s), - } -} - -const parseUpkeepPerformedLogs = (receipt: ContractReceipt) => { - const parsedLogs = [] - for (const rawLog of receipt.logs) { - try { - const log = registry.interface.parseLog(rawLog) - if ( - log.name == - registry.interface.events[ - 'UpkeepPerformed(uint256,bool,uint96,uint256,uint256,bytes)' - ].name - ) { - parsedLogs.push(log as unknown as UpkeepPerformedEvent) - } - } catch { - continue - } - } - return parsedLogs -} - -const parseReorgedUpkeepReportLogs = (receipt: ContractReceipt) => { - const parsedLogs = [] - for (const rawLog of receipt.logs) { - try { - const log = registry.interface.parseLog(rawLog) - if ( - log.name == - registry.interface.events['ReorgedUpkeepReport(uint256,bytes)'].name - ) { - parsedLogs.push(log as unknown as ReorgedUpkeepReportEvent) - } - } catch { - continue - } - } - return parsedLogs -} - -const parseStaleUpkeepReportLogs = (receipt: ContractReceipt) => { - const parsedLogs = [] - for (const rawLog of receipt.logs) { - try { - const log = registry.interface.parseLog(rawLog) - if ( - log.name == - registry.interface.events['StaleUpkeepReport(uint256,bytes)'].name - ) { - parsedLogs.push(log as unknown as StaleUpkeepReportEvent) - } - } catch { - continue - } - } - return parsedLogs -} - -const parseCancelledUpkeepReportLogs = (receipt: ContractReceipt) => { - const parsedLogs = [] - for (const rawLog of receipt.logs) { - try { - const log = registry.interface.parseLog(rawLog) - if ( - log.name == - registry.interface.events['CancelledUpkeepReport(uint256,bytes)'].name - ) { - parsedLogs.push(log as unknown as CancelledUpkeepReportEvent) - } - } catch { - continue - } - } - return parsedLogs -} - -describe('AutomationRegistry2_2', () => { - let owner: Signer - let keeper1: Signer - let keeper2: Signer - let keeper3: Signer - let keeper4: Signer - let keeper5: Signer - let nonkeeper: Signer - let signer1: Wallet - let signer2: Wallet - let signer3: Wallet - let signer4: Wallet - let signer5: Wallet - let admin: Signer - let payee1: Signer - let payee2: Signer - let payee3: Signer - let payee4: Signer - let payee5: Signer - - let upkeepId: BigNumber // conditional upkeep - let afUpkeepId: BigNumber // auto funding upkeep - let logUpkeepId: BigNumber // log trigger upkeepID - let streamsLookupUpkeepId: BigNumber // streams lookup upkeep - const numUpkeeps = 4 // see above - let keeperAddresses: string[] - let payees: string[] - let signers: Wallet[] - let signerAddresses: string[] - let config: any - let arbConfig: any - let opConfig: any - let baseConfig: Parameters - let arbConfigParams: Parameters - let opConfigParams: Parameters - let upkeepManager: string - - before(async () => { - personas = (await getUsers()).personas - - const convFactory = await ethers.getContractFactory( - 'AutomationCompatibleUtils', - ) - automationUtils = await convFactory.deploy() - - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', - ) - // need full path because there are two contracts with name MockV3Aggregator - mockV3AggregatorFactory = (await ethers.getContractFactory( - 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator', - )) as unknown as MockV3AggregatorFactory - mockArbGasInfoFactory = await ethers.getContractFactory('MockArbGasInfo') - mockOVMGasPriceOracleFactory = await ethers.getContractFactory( - 'MockOVMGasPriceOracle', - ) - upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') - upkeepAutoFunderFactory = - await ethers.getContractFactory('UpkeepAutoFunder') - chainModuleBaseFactory = await ethers.getContractFactory('ChainModuleBase') - arbitrumModuleFactory = await ethers.getContractFactory('ArbitrumModule') - optimismModuleV2Factory = - await ethers.getContractFactory('OptimismModuleV2') - streamsLookupUpkeepFactory = await ethers.getContractFactory( - 'StreamsLookupUpkeep', - ) - - owner = personas.Default - keeper1 = personas.Carol - keeper2 = personas.Eddy - keeper3 = personas.Nancy - keeper4 = personas.Norbert - keeper5 = personas.Nick - nonkeeper = personas.Ned - admin = personas.Neil - payee1 = personas.Nelly - payee2 = personas.Norbert - payee3 = personas.Nick - payee4 = personas.Eddy - payee5 = personas.Carol - upkeepManager = await personas.Norbert.getAddress() - // signers - signer1 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000001', - ) - signer2 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000002', - ) - signer3 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000003', - ) - signer4 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000004', - ) - signer5 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000005', - ) - - keeperAddresses = [ - await keeper1.getAddress(), - await keeper2.getAddress(), - await keeper3.getAddress(), - await keeper4.getAddress(), - await keeper5.getAddress(), - ] - payees = [ - await payee1.getAddress(), - await payee2.getAddress(), - await payee3.getAddress(), - await payee4.getAddress(), - await payee5.getAddress(), - ] - signers = [signer1, signer2, signer3, signer4, signer5] - - // We append 26 random addresses to keepers, payees and signers to get a system of 31 oracles - // This allows f value of 1 - 10 - for (let i = 0; i < 26; i++) { - keeperAddresses.push(randomAddress()) - payees.push(randomAddress()) - signers.push(ethers.Wallet.createRandom()) - } - signerAddresses = [] - for (const signer of signers) { - signerAddresses.push(await signer.getAddress()) - } - - logTriggerConfig = - '0x' + - automationUtils.interface - .encodeFunctionData('_logTriggerConfig', [ - { - contractAddress: randomAddress(), - filterSelector: 0, - topic0: ethers.utils.randomBytes(32), - topic1: ethers.utils.randomBytes(32), - topic2: ethers.utils.randomBytes(32), - topic3: ethers.utils.randomBytes(32), - }, - ]) - .slice(10) - }) - - // This function is similar to registry's _calculatePaymentAmount - // It uses global fastGasWei, linkEth, and assumes isExecution = false (gasFee = fastGasWei*multiplier) - // rest of the parameters are the same - const linkForGas = ( - upkeepGasSpent: BigNumber, - gasOverhead: BigNumber, - gasMultiplier: BigNumber, - premiumPPB: BigNumber, - flatFee: BigNumber, - l1CostWei?: BigNumber, - ) => { - l1CostWei = l1CostWei === undefined ? BigNumber.from(0) : l1CostWei - - const gasSpent = gasOverhead.add(BigNumber.from(upkeepGasSpent)) - const base = gasWei - .mul(gasMultiplier) - .mul(gasSpent) - .mul(linkDivisibility) - .div(linkEth) - const l1Fee = l1CostWei.mul(linkDivisibility).div(linkEth) - const gasPayment = base.add(l1Fee) - - const premium = gasWei - .mul(gasMultiplier) - .mul(upkeepGasSpent) - .add(l1CostWei) - .mul(linkDivisibility) - .div(linkEth) - .mul(premiumPPB) - .div(paymentPremiumBase) - .add(BigNumber.from(flatFee).mul('1000000000000')) - - return { - total: gasPayment.add(premium), - gasPayment, - premium, - } - } - - const verifyMaxPayment = async ( - registry: IAutomationRegistry, - chainModule: IChainModule, - maxl1CostWeWithoutMultiplier?: BigNumber, - ) => { - type TestCase = { - name: string - multiplier: number - gas: number - premium: number - flatFee: number - } - - const tests: TestCase[] = [ - { - name: 'no fees', - multiplier: 1, - gas: 100000, - premium: 0, - flatFee: 0, - }, - { - name: 'basic fees', - multiplier: 1, - gas: 100000, - premium: 250000000, - flatFee: 1000000, - }, - { - name: 'max fees', - multiplier: 3, - gas: 10000000, - premium: 250000000, - flatFee: 1000000, - }, - ] - - const fPlusOne = BigNumber.from(f + 1) - const chainModuleOverheads = await chainModule.getGasOverhead() - const totalConditionalOverhead = registryConditionalOverhead - .add(registryPerSignerGasOverhead.mul(fPlusOne)) - .add( - registryPerPerformByteGasOverhead - .add(chainModuleOverheads.chainModulePerByteOverhead) - .mul( - maxPerformDataSize - .add(registryTransmitCalldataFixedBytesOverhead) - .add( - registryTransmitCalldataPerSignerBytesOverhead.mul(fPlusOne), - ), - ), - ) - .add(chainModuleOverheads.chainModuleFixedOverhead) - - const totalLogOverhead = registryLogOverhead - .add(registryPerSignerGasOverhead.mul(fPlusOne)) - .add( - registryPerPerformByteGasOverhead - .add(chainModuleOverheads.chainModulePerByteOverhead) - .mul( - maxPerformDataSize - .add(registryTransmitCalldataFixedBytesOverhead) - .add( - registryTransmitCalldataPerSignerBytesOverhead.mul(fPlusOne), - ), - ), - ) - .add(chainModuleOverheads.chainModuleFixedOverhead) - - for (const test of tests) { - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - { - paymentPremiumPPB: test.premium, - flatFeeMicroLink: test.flatFee, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier: test.multiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModule.address, - reorgProtectionEnabled: true, - }, - offchainVersion, - offchainBytes, - ) - - const conditionalPrice = await registry.getMaxPaymentForGas( - Trigger.CONDITION, - test.gas, - ) - expect(conditionalPrice).to.equal( - linkForGas( - BigNumber.from(test.gas), - totalConditionalOverhead, - BigNumber.from(test.multiplier), - BigNumber.from(test.premium), - BigNumber.from(test.flatFee), - maxl1CostWeWithoutMultiplier?.mul(BigNumber.from(test.multiplier)), - ).total, - ) - - const logPrice = await registry.getMaxPaymentForGas(Trigger.LOG, test.gas) - expect(logPrice).to.equal( - linkForGas( - BigNumber.from(test.gas), - totalLogOverhead, - BigNumber.from(test.multiplier), - BigNumber.from(test.premium), - BigNumber.from(test.flatFee), - maxl1CostWeWithoutMultiplier?.mul(BigNumber.from(test.multiplier)), - ).total, - ) - } - } - - const verifyConsistentAccounting = async ( - maxAllowedSpareChange: BigNumber, - ) => { - const expectedLinkBalance = (await registry.getState()).state - .expectedLinkBalance - const linkTokenBalance = await linkToken.balanceOf(registry.address) - const upkeepIdBalance = (await registry.getUpkeep(upkeepId)).balance - let totalKeeperBalance = BigNumber.from(0) - for (let i = 0; i < keeperAddresses.length; i++) { - totalKeeperBalance = totalKeeperBalance.add( - (await registry.getTransmitterInfo(keeperAddresses[i])).balance, - ) - } - const ownerBalance = (await registry.getState()).state.ownerLinkBalance - assert.isTrue(expectedLinkBalance.eq(linkTokenBalance)) - assert.isTrue( - upkeepIdBalance - .add(totalKeeperBalance) - .add(ownerBalance) - .lte(expectedLinkBalance), - ) - assert.isTrue( - expectedLinkBalance - .sub(upkeepIdBalance) - .sub(totalKeeperBalance) - .sub(ownerBalance) - .lte(maxAllowedSpareChange), - ) - } - - interface GetTransmitTXOptions { - numSigners?: number - startingSignerIndex?: number - gasLimit?: BigNumberish - gasPrice?: BigNumberish - performGas?: BigNumberish - performDatas?: string[] - checkBlockNum?: number - checkBlockHash?: string - logBlockHash?: BytesLike - txHash?: BytesLike - logIndex?: number - timestamp?: number - } - - const getTransmitTx = async ( - registry: IAutomationRegistry, - transmitter: Signer, - upkeepIds: BigNumber[], - overrides: GetTransmitTXOptions = {}, - ) => { - const latestBlock = await ethers.provider.getBlock('latest') - const configDigest = (await registry.getState()).state.latestConfigDigest - const config = { - numSigners: f + 1, - startingSignerIndex: 0, - performDatas: undefined, - performGas, - checkBlockNum: latestBlock.number, - checkBlockHash: latestBlock.hash, - logIndex: 0, - txHash: undefined, // assigned uniquely below - logBlockHash: undefined, // assigned uniquely below - timestamp: now(), - gasLimit: undefined, - gasPrice: undefined, - } - Object.assign(config, overrides) - const upkeeps: UpkeepData[] = [] - for (let i = 0; i < upkeepIds.length; i++) { - let trigger: string - switch (getTriggerType(upkeepIds[i])) { - case Trigger.CONDITION: - trigger = encodeBlockTrigger({ - blockNum: config.checkBlockNum, - blockHash: config.checkBlockHash, - }) - break - case Trigger.LOG: - trigger = encodeLogTrigger({ - logBlockHash: config.logBlockHash || ethers.utils.randomBytes(32), - txHash: config.txHash || ethers.utils.randomBytes(32), - logIndex: config.logIndex, - blockNum: config.checkBlockNum, - blockHash: config.checkBlockHash, - }) - break - } - upkeeps.push({ - Id: upkeepIds[i], - performGas: config.performGas, - trigger, - performData: config.performDatas ? config.performDatas[i] : '0x', - }) - } - - const report = makeReport(upkeeps) - const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] - const sigs = signReport( - reportContext, - report, - signers.slice( - config.startingSignerIndex, - config.startingSignerIndex + config.numSigners, - ), - ) - - type txOverride = { - gasLimit?: BigNumberish | Promise - gasPrice?: BigNumberish | Promise - } - const txOverrides: txOverride = {} - if (config.gasLimit) { - txOverrides.gasLimit = config.gasLimit - } - if (config.gasPrice) { - txOverrides.gasPrice = config.gasPrice - } - - return registry - .connect(transmitter) - .transmit( - [configDigest, epochAndRound5_1, emptyBytes32], - report, - sigs.rs, - sigs.ss, - sigs.vs, - txOverrides, - ) - } - - const getTransmitTxWithReport = async ( - registry: IAutomationRegistry, - transmitter: Signer, - report: BytesLike, - ) => { - const configDigest = (await registry.getState()).state.latestConfigDigest - const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] - const sigs = signReport(reportContext, report, signers.slice(0, f + 1)) - - return registry - .connect(transmitter) - .transmit( - [configDigest, epochAndRound5_1, emptyBytes32], - report, - sigs.rs, - sigs.ss, - sigs.vs, - ) - } - - const setup = async () => { - linkToken = await linkTokenFactory.connect(owner).deploy() - gasPriceFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(0, gasWei) - linkEthFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(9, linkEth) - const upkeepTranscoderFactory = await ethers.getContractFactory( - 'UpkeepTranscoder4_0', - ) - transcoder = await upkeepTranscoderFactory.connect(owner).deploy() - mockArbGasInfo = await mockArbGasInfoFactory.connect(owner).deploy() - mockOVMGasPriceOracle = await mockOVMGasPriceOracleFactory - .connect(owner) - .deploy() - chainModuleBase = await chainModuleBaseFactory.connect(owner).deploy() - arbitrumModule = await arbitrumModuleFactory.connect(owner).deploy() - optimismModule = await optimismModuleV2Factory.connect(owner).deploy() - streamsLookupUpkeep = await streamsLookupUpkeepFactory - .connect(owner) - .deploy( - BigNumber.from('10000'), - BigNumber.from('100'), - false /* useArbBlock */, - true /* staging */, - false /* verify mercury response */, - ) - - const arbOracleCode = await ethers.provider.send('eth_getCode', [ - mockArbGasInfo.address, - ]) - await ethers.provider.send('hardhat_setCode', [ - '0x000000000000000000000000000000000000006C', - arbOracleCode, - ]) - - const optOracleCode = await ethers.provider.send('eth_getCode', [ - mockOVMGasPriceOracle.address, - ]) - await ethers.provider.send('hardhat_setCode', [ - '0x420000000000000000000000000000000000000F', - optOracleCode, - ]) - - const mockArbSys = await new MockArbSysFactory(owner).deploy() - const arbSysCode = await ethers.provider.send('eth_getCode', [ - mockArbSys.address, - ]) - await ethers.provider.send('hardhat_setCode', [ - '0x0000000000000000000000000000000000000064', - arbSysCode, - ]) - - config = { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - } - - arbConfig = { ...config } - arbConfig.chainModule = arbitrumModule.address - opConfig = { ...config } - opConfig.chainModule = optimismModule.address - - baseConfig = [ - signerAddresses, - keeperAddresses, - f, - config, - offchainVersion, - offchainBytes, - ] - arbConfigParams = [ - signerAddresses, - keeperAddresses, - f, - arbConfig, - offchainVersion, - offchainBytes, - ] - opConfigParams = [ - signerAddresses, - keeperAddresses, - f, - opConfig, - offchainVersion, - offchainBytes, - ] - - registry = await deployRegistry22( - owner, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - zeroAddress, - ) - - arbRegistry = await deployRegistry22( - owner, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - zeroAddress, - ) - - opRegistry = await deployRegistry22( - owner, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - zeroAddress, - ) - - mgRegistry = await deployRegistry22( - owner, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - zeroAddress, - ) - - blankRegistry = await deployRegistry22( - owner, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - zeroAddress, - ) - - registryConditionalOverhead = await registry.getConditionalGasOverhead() - registryLogOverhead = await registry.getLogGasOverhead() - registryPerSignerGasOverhead = await registry.getPerSignerGasOverhead() - registryPerPerformByteGasOverhead = - await registry.getPerPerformByteGasOverhead() - registryTransmitCalldataFixedBytesOverhead = - await registry.getTransmitCalldataFixedBytesOverhead() - registryTransmitCalldataPerSignerBytesOverhead = - await registry.getTransmitCalldataPerSignerBytesOverhead() - cancellationDelay = (await registry.getCancellationDelay()).toNumber() - - await registry.connect(owner).setConfigTypeSafe(...baseConfig) - await mgRegistry.connect(owner).setConfigTypeSafe(...baseConfig) - await arbRegistry.connect(owner).setConfigTypeSafe(...arbConfigParams) - await opRegistry.connect(owner).setConfigTypeSafe(...opConfigParams) - for (const reg of [registry, arbRegistry, opRegistry, mgRegistry]) { - await reg.connect(owner).setPayees(payees) - await linkToken.connect(admin).approve(reg.address, toWei('1000')) - await linkToken.connect(owner).approve(reg.address, toWei('1000')) - } - - mock = await upkeepMockFactory.deploy() - await linkToken - .connect(owner) - .transfer(await admin.getAddress(), toWei('1000')) - let tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') - upkeepId = await getUpkeepID(tx) - - autoFunderUpkeep = await upkeepAutoFunderFactory - .connect(owner) - .deploy(linkToken.address, registry.address) - tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](autoFunderUpkeep.address, performGas, autoFunderUpkeep.address, randomBytes, '0x') - afUpkeepId = await getUpkeepID(tx) - - ltUpkeep = await deployMockContract(owner, ILogAutomationactory.abi) - tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,uint8,bytes,bytes,bytes)' - ](ltUpkeep.address, performGas, await admin.getAddress(), Trigger.LOG, '0x', logTriggerConfig, emptyBytes) - logUpkeepId = await getUpkeepID(tx) - - await autoFunderUpkeep.setUpkeepId(afUpkeepId) - // Give enough funds for upkeep as well as to the upkeep contract - await linkToken - .connect(owner) - .transfer(autoFunderUpkeep.address, toWei('1000')) - - tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](streamsLookupUpkeep.address, performGas, await admin.getAddress(), randomBytes, '0x') - streamsLookupUpkeepId = await getUpkeepID(tx) - } - - const getMultipleUpkeepsDeployedAndFunded = async ( - numPassingConditionalUpkeeps: number, - numPassingLogUpkeeps: number, - numFailingUpkeeps: number, - ) => { - const passingConditionalUpkeepIds = [] - const passingLogUpkeepIds = [] - const failingUpkeepIds = [] - for (let i = 0; i < numPassingConditionalUpkeeps; i++) { - const mock = await upkeepMockFactory.deploy() - await mock.setCanPerform(true) - await mock.setPerformGasToBurn(BigNumber.from('0')) - const tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') - const condUpkeepId = await getUpkeepID(tx) - passingConditionalUpkeepIds.push(condUpkeepId) - - // Add funds to passing upkeeps - await registry.connect(admin).addFunds(condUpkeepId, toWei('100')) - } - for (let i = 0; i < numPassingLogUpkeeps; i++) { - const mock = await upkeepMockFactory.deploy() - await mock.setCanPerform(true) - await mock.setPerformGasToBurn(BigNumber.from('0')) - const tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,uint8,bytes,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), Trigger.LOG, '0x', logTriggerConfig, emptyBytes) - const logUpkeepId = await getUpkeepID(tx) - passingLogUpkeepIds.push(logUpkeepId) - - // Add funds to passing upkeeps - await registry.connect(admin).addFunds(logUpkeepId, toWei('100')) - } - for (let i = 0; i < numFailingUpkeeps; i++) { - const mock = await upkeepMockFactory.deploy() - await mock.setCanPerform(true) - await mock.setPerformGasToBurn(BigNumber.from('0')) - const tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') - const failingUpkeepId = await getUpkeepID(tx) - failingUpkeepIds.push(failingUpkeepId) - } - return { - passingConditionalUpkeepIds, - passingLogUpkeepIds, - failingUpkeepIds, - } - } - - beforeEach(async () => { - await loadFixture(setup) - }) - - describe('#transmit', () => { - const fArray = [1, 5, 10] - - it('reverts when registry is paused', async () => { - await registry.connect(owner).pause() - await evmRevertCustomError( - getTransmitTx(registry, keeper1, [upkeepId]), - registry, - 'RegistryPaused', - ) - }) - - it('reverts when called by non active transmitter', async () => { - await evmRevertCustomError( - getTransmitTx(registry, payee1, [upkeepId]), - registry, - 'OnlyActiveTransmitters', - ) - }) - - it('reverts when report data lengths mismatches', async () => { - const upkeepIds = [] - const gasLimits: BigNumber[] = [] - const triggers: string[] = [] - const performDatas = [] - - upkeepIds.push(upkeepId) - gasLimits.push(performGas) - triggers.push('0x') - performDatas.push('0x') - // Push an extra perform data - performDatas.push('0x') - - const report = encodeReport({ - fastGasWei: 0, - linkNative: 0, - upkeepIds, - gasLimits, - triggers, - performDatas, - }) - - await evmRevertCustomError( - getTransmitTxWithReport(registry, keeper1, report), - registry, - 'InvalidReport', - ) - }) - - it('returns early when invalid upkeepIds are included in report', async () => { - const tx = await getTransmitTx(registry, keeper1, [ - upkeepId.add(BigNumber.from('1')), - ]) - - const receipt = await tx.wait() - const cancelledUpkeepReportLogs = parseCancelledUpkeepReportLogs(receipt) - // exactly 1 CancelledUpkeepReport log should be emitted - assert.equal(cancelledUpkeepReportLogs.length, 1) - }) - - it('performs even when the upkeep has insufficient funds and the upkeep pays out all the remaining balance', async () => { - // add very little fund to this upkeep - await registry.connect(admin).addFunds(upkeepId, BigNumber.from(10)) - const tx = await getTransmitTx(registry, keeper1, [upkeepId]) - const receipt = await tx.wait() - // the upkeep is underfunded in transmit but still performed - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - assert.equal(upkeepPerformedLogs.length, 1) - const balance = (await registry.getUpkeep(upkeepId)).balance - assert.equal(balance.toNumber(), 0) - }) - - context('When the upkeep is funded', async () => { - beforeEach(async () => { - // Fund the upkeep - await Promise.all([ - registry.connect(admin).addFunds(upkeepId, toWei('100')), - registry.connect(admin).addFunds(logUpkeepId, toWei('100')), - ]) - }) - - it('handles duplicate upkeepIDs', async () => { - const tests: [string, BigNumber, number, number][] = [ - // [name, upkeep, num stale, num performed] - ['conditional', upkeepId, 1, 1], // checkBlocks must be sequential - ['log-trigger', logUpkeepId, 0, 2], // logs are deduped based on the "trigger ID" - ] - for (const [type, id, nStale, nPerformed] of tests) { - const tx = await getTransmitTx(registry, keeper1, [id, id]) - const receipt = await tx.wait() - const staleUpkeepReport = parseStaleUpkeepReportLogs(receipt) - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - assert.equal( - staleUpkeepReport.length, - nStale, - `wrong log count for ${type} upkeep`, - ) - assert.equal( - upkeepPerformedLogs.length, - nPerformed, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('handles duplicate log triggers', async () => { - const logBlockHash = ethers.utils.randomBytes(32) - const txHash = ethers.utils.randomBytes(32) - const logIndex = 0 - const expectedDedupKey = ethers.utils.solidityKeccak256( - ['uint256', 'bytes32', 'bytes32', 'uint32'], - [logUpkeepId, logBlockHash, txHash, logIndex], - ) - assert.isFalse(await registry.hasDedupKey(expectedDedupKey)) - const tx = await getTransmitTx( - registry, - keeper1, - [logUpkeepId, logUpkeepId], - { logBlockHash, txHash, logIndex }, // will result in the same dedup key - ) - const receipt = await tx.wait() - const staleUpkeepReport = parseStaleUpkeepReportLogs(receipt) - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - assert.equal(staleUpkeepReport.length, 1) - assert.equal(upkeepPerformedLogs.length, 1) - assert.isTrue(await registry.hasDedupKey(expectedDedupKey)) - await expect(tx) - .to.emit(registry, 'DedupKeyAdded') - .withArgs(expectedDedupKey) - }) - - it('returns early when check block number is less than last perform (block)', async () => { - // First perform an upkeep to put last perform block number on upkeep state - const tx = await getTransmitTx(registry, keeper1, [upkeepId]) - await tx.wait() - const lastPerformed = (await registry.getUpkeep(upkeepId)) - .lastPerformedBlockNumber - const lastPerformBlock = await ethers.provider.getBlock(lastPerformed) - assert.equal(lastPerformed.toString(), tx.blockNumber?.toString()) - // Try to transmit a report which has checkBlockNumber = lastPerformed-1, should result in stale report - const transmitTx = await getTransmitTx(registry, keeper1, [upkeepId], { - checkBlockNum: lastPerformBlock.number - 1, - checkBlockHash: lastPerformBlock.parentHash, - }) - const receipt = await transmitTx.wait() - const staleUpkeepReportLogs = parseStaleUpkeepReportLogs(receipt) - // exactly 1 StaleUpkeepReportLogs log should be emitted - assert.equal(staleUpkeepReportLogs.length, 1) - }) - - it('handles case when check block hash does not match', async () => { - const tests: [string, BigNumber][] = [ - ['conditional', upkeepId], - ['log-trigger', logUpkeepId], - ] - for (const [type, id] of tests) { - const latestBlock = await ethers.provider.getBlock('latest') - // Try to transmit a report which has incorrect checkBlockHash - const tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: latestBlock.number - 1, - checkBlockHash: latestBlock.hash, // should be latestBlock.parentHash - }) - - const receipt = await tx.wait() - const reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) - // exactly 1 ReorgedUpkeepReportLogs log should be emitted - assert.equal( - reorgedUpkeepReportLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('handles case when check block number is older than 256 blocks', async () => { - for (let i = 0; i < 256; i++) { - await ethers.provider.send('evm_mine', []) - } - const tests: [string, BigNumber][] = [ - ['conditional', upkeepId], - ['log-trigger', logUpkeepId], - ] - for (const [type, id] of tests) { - const latestBlock = await ethers.provider.getBlock('latest') - const old = await ethers.provider.getBlock(latestBlock.number - 256) - // Try to transmit a report which has incorrect checkBlockHash - const tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: old.number, - checkBlockHash: old.hash, - }) - - const receipt = await tx.wait() - const reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) - // exactly 1 ReorgedUpkeepReportLogs log should be emitted - assert.equal( - reorgedUpkeepReportLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('allows bypassing reorg protection with empty blockhash', async () => { - const tests: [string, BigNumber][] = [ - ['conditional', upkeepId], - ['log-trigger', logUpkeepId], - ] - for (const [type, id] of tests) { - const latestBlock = await ethers.provider.getBlock('latest') - const tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: latestBlock.number, - checkBlockHash: emptyBytes32, - }) - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - assert.equal( - upkeepPerformedLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('allows bypassing reorg protection with reorgProtectionEnabled false config', async () => { - const tests: [string, BigNumber][] = [ - ['conditional', upkeepId], - ['log-trigger', logUpkeepId], - ] - const newConfig = config - newConfig.reorgProtectionEnabled = false - await registry // used to test initial configurations - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - newConfig, - offchainVersion, - offchainBytes, - ) - - for (const [type, id] of tests) { - const latestBlock = await ethers.provider.getBlock('latest') - // Try to transmit a report which has incorrect checkBlockHash - const tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: latestBlock.number - 1, - checkBlockHash: latestBlock.hash, // should be latestBlock.parentHash - }) - - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - assert.equal( - upkeepPerformedLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('allows very old trigger block numbers when bypassing reorg protection with reorgProtectionEnabled config', async () => { - const newConfig = config - newConfig.reorgProtectionEnabled = false - await registry // used to test initial configurations - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - newConfig, - offchainVersion, - offchainBytes, - ) - for (let i = 0; i < 256; i++) { - await ethers.provider.send('evm_mine', []) - } - const tests: [string, BigNumber][] = [ - ['conditional', upkeepId], - ['log-trigger', logUpkeepId], - ] - for (const [type, id] of tests) { - const latestBlock = await ethers.provider.getBlock('latest') - const old = await ethers.provider.getBlock(latestBlock.number - 256) - // Try to transmit a report which has incorrect checkBlockHash - const tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: old.number, - checkBlockHash: old.hash, - }) - - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - assert.equal( - upkeepPerformedLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('allows very old trigger block numbers when bypassing reorg protection with empty blockhash', async () => { - // mine enough blocks so that blockhash(1) is unavailable - for (let i = 0; i <= 256; i++) { - await ethers.provider.send('evm_mine', []) - } - const tests: [string, BigNumber][] = [ - ['conditional', upkeepId], - ['log-trigger', logUpkeepId], - ] - for (const [type, id] of tests) { - const tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: 1, - checkBlockHash: emptyBytes32, - }) - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - assert.equal( - upkeepPerformedLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('returns early when future block number is provided as trigger, irrespective of blockhash being present', async () => { - const tests: [string, BigNumber][] = [ - ['conditional', upkeepId], - ['log-trigger', logUpkeepId], - ] - for (const [type, id] of tests) { - const latestBlock = await ethers.provider.getBlock('latest') - - // Should fail when blockhash is empty - let tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: latestBlock.number + 100, - checkBlockHash: emptyBytes32, - }) - let receipt = await tx.wait() - let reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) - // exactly 1 ReorgedUpkeepReportLogs log should be emitted - assert.equal( - reorgedUpkeepReportLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - - // Should also fail when blockhash is not empty - tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: latestBlock.number + 100, - checkBlockHash: latestBlock.hash, - }) - receipt = await tx.wait() - reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) - // exactly 1 ReorgedUpkeepReportLogs log should be emitted - assert.equal( - reorgedUpkeepReportLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('returns early when future block number is provided as trigger, irrespective of reorgProtectionEnabled config', async () => { - const newConfig = config - newConfig.reorgProtectionEnabled = false - await registry // used to test initial configurations - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - newConfig, - offchainVersion, - offchainBytes, - ) - const tests: [string, BigNumber][] = [ - ['conditional', upkeepId], - ['log-trigger', logUpkeepId], - ] - for (const [type, id] of tests) { - const latestBlock = await ethers.provider.getBlock('latest') - - // Should fail when blockhash is empty - let tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: latestBlock.number + 100, - checkBlockHash: emptyBytes32, - }) - let receipt = await tx.wait() - let reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) - // exactly 1 ReorgedUpkeepReportLogs log should be emitted - assert.equal( - reorgedUpkeepReportLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - - // Should also fail when blockhash is not empty - tx = await getTransmitTx(registry, keeper1, [id], { - checkBlockNum: latestBlock.number + 100, - checkBlockHash: latestBlock.hash, - }) - receipt = await tx.wait() - reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) - // exactly 1 ReorgedUpkeepReportLogs log should be emitted - assert.equal( - reorgedUpkeepReportLogs.length, - 1, - `wrong log count for ${type} upkeep`, - ) - } - }) - - it('returns early when upkeep is cancelled and cancellation delay has gone', async () => { - const latestBlockReport = await makeLatestBlockReport([upkeepId]) - await registry.connect(admin).cancelUpkeep(upkeepId) - - for (let i = 0; i < cancellationDelay; i++) { - await ethers.provider.send('evm_mine', []) - } - - const tx = await getTransmitTxWithReport( - registry, - keeper1, - latestBlockReport, - ) - - const receipt = await tx.wait() - const cancelledUpkeepReportLogs = - parseCancelledUpkeepReportLogs(receipt) - // exactly 1 CancelledUpkeepReport log should be emitted - assert.equal(cancelledUpkeepReportLogs.length, 1) - }) - - it('does not revert if the target cannot execute', async () => { - await mock.setCanPerform(false) - const tx = await getTransmitTx(registry, keeper1, [upkeepId]) - - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly 1 Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, 1) - const upkeepPerformedLog = upkeepPerformedLogs[0] - - const success = upkeepPerformedLog.args.success - assert.equal(success, false) - }) - - it('does not revert if the target runs out of gas', async () => { - await mock.setCanPerform(false) - - const tx = await getTransmitTx(registry, keeper1, [upkeepId], { - performGas: 10, // too little gas - }) - - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly 1 Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, 1) - const upkeepPerformedLog = upkeepPerformedLogs[0] - - const success = upkeepPerformedLog.args.success - assert.equal(success, false) - }) - - it('reverts if not enough gas supplied', async () => { - await evmRevert( - getTransmitTx(registry, keeper1, [upkeepId], { - gasLimit: performGas, - }), - ) - }) - - it('executes the data passed to the registry', async () => { - await mock.setCanPerform(true) - - const tx = await getTransmitTx(registry, keeper1, [upkeepId], { - performDatas: [randomBytes], - }) - const receipt = await tx.wait() - - const upkeepPerformedWithABI = [ - 'event UpkeepPerformedWith(bytes upkeepData)', - ] - const iface = new ethers.utils.Interface(upkeepPerformedWithABI) - const parsedLogs = [] - for (let i = 0; i < receipt.logs.length; i++) { - const log = receipt.logs[i] - try { - parsedLogs.push(iface.parseLog(log)) - } catch (e) { - // ignore log - } - } - assert.equal(parsedLogs.length, 1) - assert.equal(parsedLogs[0].args.upkeepData, randomBytes) - }) - - it('uses actual execution price for payment and premium calculation', async () => { - // Actual multiplier is 2, but we set gasPrice to be 1x gasWei - const gasPrice = gasWei.mul(BigNumber.from('1')) - await mock.setCanPerform(true) - const registryPremiumBefore = (await registry.getState()).state - .totalPremium - const tx = await getTransmitTx(registry, keeper1, [upkeepId], { - gasPrice, - }) - const receipt = await tx.wait() - const registryPremiumAfter = (await registry.getState()).state - .totalPremium - const premium = registryPremiumAfter.sub(registryPremiumBefore) - - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly 1 Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, 1) - const upkeepPerformedLog = upkeepPerformedLogs[0] - - const gasUsed = upkeepPerformedLog.args.gasUsed - const gasOverhead = upkeepPerformedLog.args.gasOverhead - const totalPayment = upkeepPerformedLog.args.totalPayment - - assert.equal( - linkForGas( - gasUsed, - gasOverhead, - BigNumber.from('1'), // Not the config multiplier, but the actual gas used - paymentPremiumPPB, - flatFeeMicroLink, - ).total.toString(), - totalPayment.toString(), - ) - - assert.equal( - linkForGas( - gasUsed, - gasOverhead, - BigNumber.from('1'), // Not the config multiplier, but the actual gas used - paymentPremiumPPB, - flatFeeMicroLink, - ).premium.toString(), - premium.toString(), - ) - }) - - it('only pays at a rate up to the gas ceiling [ @skip-coverage ]', async () => { - // Actual multiplier is 2, but we set gasPrice to be 10x - const gasPrice = gasWei.mul(BigNumber.from('10')) - await mock.setCanPerform(true) - - const tx = await getTransmitTx(registry, keeper1, [upkeepId], { - gasPrice, - }) - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly 1 Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, 1) - const upkeepPerformedLog = upkeepPerformedLogs[0] - - const gasUsed = upkeepPerformedLog.args.gasUsed - const gasOverhead = upkeepPerformedLog.args.gasOverhead - const totalPayment = upkeepPerformedLog.args.totalPayment - - assert.equal( - linkForGas( - gasUsed, - gasOverhead, - gasCeilingMultiplier, // Should be same with exisitng multiplier - paymentPremiumPPB, - flatFeeMicroLink, - ).total.toString(), - totalPayment.toString(), - ) - }) - - it('correctly accounts for l payment', async () => { - await mock.setCanPerform(true) - // Same as MockArbGasInfo.sol - const l1CostWeiArb = BigNumber.from(1000000) - - let tx = await arbRegistry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') - const testUpkeepId = await getUpkeepID(tx) - await arbRegistry.connect(owner).addFunds(testUpkeepId, toWei('100')) - - // Do the thing - tx = await getTransmitTx( - arbRegistry, - keeper1, - [testUpkeepId], - - { gasPrice: gasWei.mul('5') }, // High gas price so that it gets capped - ) - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly 1 Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, 1) - const upkeepPerformedLog = upkeepPerformedLogs[0] - - const gasUsed = upkeepPerformedLog.args.gasUsed - const gasOverhead = upkeepPerformedLog.args.gasOverhead - const totalPayment = upkeepPerformedLog.args.totalPayment - - assert.equal( - linkForGas( - gasUsed, - gasOverhead, - gasCeilingMultiplier, - paymentPremiumPPB, - flatFeeMicroLink, - l1CostWeiArb, - ).total.toString(), - totalPayment.toString(), - ) - }) - - itMaybe('can self fund', async () => { - const maxPayment = await registry.getMaxPaymentForGas( - Trigger.CONDITION, - performGas, - ) - - // First set auto funding amount to 0 and verify that balance is deducted upon performUpkeep - let initialBalance = toWei('100') - await registry.connect(owner).addFunds(afUpkeepId, initialBalance) - await autoFunderUpkeep.setAutoFundLink(0) - await autoFunderUpkeep.setIsEligible(true) - await getTransmitTx(registry, keeper1, [afUpkeepId]) - - let postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance - assert.isTrue(postUpkeepBalance.lt(initialBalance)) // Balance should be deducted - assert.isTrue(postUpkeepBalance.gte(initialBalance.sub(maxPayment))) // Balance should not be deducted more than maxPayment - - // Now set auto funding amount to 100 wei and verify that the balance increases - initialBalance = postUpkeepBalance - const autoTopupAmount = toWei('100') - await autoFunderUpkeep.setAutoFundLink(autoTopupAmount) - await autoFunderUpkeep.setIsEligible(true) - await getTransmitTx(registry, keeper1, [afUpkeepId]) - - postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance - // Balance should increase by autoTopupAmount and decrease by max maxPayment - assert.isTrue( - postUpkeepBalance.gte( - initialBalance.add(autoTopupAmount).sub(maxPayment), - ), - ) - }) - - it('can self cancel', async () => { - await registry.connect(owner).addFunds(afUpkeepId, toWei('100')) - - await autoFunderUpkeep.setIsEligible(true) - await autoFunderUpkeep.setShouldCancel(true) - - let registration = await registry.getUpkeep(afUpkeepId) - const oldExpiration = registration.maxValidBlocknumber - - // Do the thing - await getTransmitTx(registry, keeper1, [afUpkeepId]) - - // Verify upkeep gets cancelled - registration = await registry.getUpkeep(afUpkeepId) - const newExpiration = registration.maxValidBlocknumber - assert.isTrue(newExpiration.lt(oldExpiration)) - }) - - it('reverts when configDigest mismatches', async () => { - const report = await makeLatestBlockReport([upkeepId]) - const reportContext = [emptyBytes32, epochAndRound5_1, emptyBytes32] // wrong config digest - const sigs = signReport(reportContext, report, signers.slice(0, f + 1)) - await evmRevertCustomError( - registry - .connect(keeper1) - .transmit( - [reportContext[0], reportContext[1], reportContext[2]], - report, - sigs.rs, - sigs.ss, - sigs.vs, - ), - registry, - 'ConfigDigestMismatch', - ) - }) - - it('reverts with incorrect number of signatures', async () => { - const configDigest = (await registry.getState()).state - .latestConfigDigest - const report = await makeLatestBlockReport([upkeepId]) - const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest - const sigs = signReport(reportContext, report, signers.slice(0, f + 2)) - await evmRevertCustomError( - registry - .connect(keeper1) - .transmit( - [reportContext[0], reportContext[1], reportContext[2]], - report, - sigs.rs, - sigs.ss, - sigs.vs, - ), - registry, - 'IncorrectNumberOfSignatures', - ) - }) - - it('reverts with invalid signature for inactive signers', async () => { - const configDigest = (await registry.getState()).state - .latestConfigDigest - const report = await makeLatestBlockReport([upkeepId]) - const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest - const sigs = signReport(reportContext, report, [ - new ethers.Wallet(ethers.Wallet.createRandom()), - new ethers.Wallet(ethers.Wallet.createRandom()), - ]) - await evmRevertCustomError( - registry - .connect(keeper1) - .transmit( - [reportContext[0], reportContext[1], reportContext[2]], - report, - sigs.rs, - sigs.ss, - sigs.vs, - ), - registry, - 'OnlyActiveSigners', - ) - }) - - it('reverts with invalid signature for duplicated signers', async () => { - const configDigest = (await registry.getState()).state - .latestConfigDigest - const report = await makeLatestBlockReport([upkeepId]) - const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest - const sigs = signReport(reportContext, report, [signer1, signer1]) - await evmRevertCustomError( - registry - .connect(keeper1) - .transmit( - [reportContext[0], reportContext[1], reportContext[2]], - report, - sigs.rs, - sigs.ss, - sigs.vs, - ), - registry, - 'DuplicateSigners', - ) - }) - - itMaybe( - 'has a large enough gas overhead to cover upkeep that use all its gas [ @skip-coverage ]', - async () => { - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - 10, // maximise f to maximise overhead - config, - offchainVersion, - offchainBytes, - ) - const tx = await registry - .connect(owner) - ['registerUpkeep(address,uint32,address,bytes,bytes)']( - mock.address, - maxPerformGas, // max allowed gas - await admin.getAddress(), - randomBytes, - '0x', - ) - const testUpkeepId = await getUpkeepID(tx) - await registry.connect(admin).addFunds(testUpkeepId, toWei('100')) - - let performData = '0x' - for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { - performData += '11' - } // max allowed performData - - await mock.setCanPerform(true) - await mock.setPerformGasToBurn(maxPerformGas) - - await getTransmitTx(registry, keeper1, [testUpkeepId], { - gasLimit: maxPerformGas.add(transmitGasOverhead), - numSigners: 11, - performDatas: [performData], - }) // Should not revert - }, - ) - - itMaybe( - 'performs upkeep, deducts payment, updates lastPerformed and emits events', - async () => { - await mock.setCanPerform(true) - - for (const i in fArray) { - const newF = fArray[i] - await registry - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - newF, - config, - offchainVersion, - offchainBytes, - ) - const checkBlock = await ethers.provider.getBlock('latest') - - const keeperBefore = await registry.getTransmitterInfo( - await keeper1.getAddress(), - ) - const registrationBefore = await registry.getUpkeep(upkeepId) - const registryPremiumBefore = (await registry.getState()).state - .totalPremium - const keeperLinkBefore = await linkToken.balanceOf( - await keeper1.getAddress(), - ) - const registryLinkBefore = await linkToken.balanceOf( - registry.address, - ) - - // Do the thing - const tx = await getTransmitTx(registry, keeper1, [upkeepId], { - checkBlockNum: checkBlock.number, - checkBlockHash: checkBlock.hash, - numSigners: newF + 1, - }) - - const receipt = await tx.wait() - - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly 1 Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, 1) - const upkeepPerformedLog = upkeepPerformedLogs[0] - - const id = upkeepPerformedLog.args.id - const success = upkeepPerformedLog.args.success - const trigger = upkeepPerformedLog.args.trigger - const gasUsed = upkeepPerformedLog.args.gasUsed - const gasOverhead = upkeepPerformedLog.args.gasOverhead - const totalPayment = upkeepPerformedLog.args.totalPayment - assert.equal(id.toString(), upkeepId.toString()) - assert.equal(success, true) - assert.equal( - trigger, - encodeBlockTrigger({ - blockNum: checkBlock.number, - blockHash: checkBlock.hash, - }), - ) - assert.isTrue(gasUsed.gt(BigNumber.from('0'))) - assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) - assert.isTrue(totalPayment.gt(BigNumber.from('0'))) - - const keeperAfter = await registry.getTransmitterInfo( - await keeper1.getAddress(), - ) - const registrationAfter = await registry.getUpkeep(upkeepId) - const keeperLinkAfter = await linkToken.balanceOf( - await keeper1.getAddress(), - ) - const registryLinkAfter = await linkToken.balanceOf( - registry.address, - ) - const registryPremiumAfter = (await registry.getState()).state - .totalPremium - const premium = registryPremiumAfter.sub(registryPremiumBefore) - // Keeper payment is gasPayment + premium / num keepers - const keeperPayment = totalPayment - .sub(premium) - .add(premium.div(BigNumber.from(keeperAddresses.length))) - - assert.equal( - keeperAfter.balance.sub(keeperPayment).toString(), - keeperBefore.balance.toString(), - ) - assert.equal( - registrationBefore.balance.sub(totalPayment).toString(), - registrationAfter.balance.toString(), - ) - assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) - assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) - - // Amount spent should be updated correctly - assert.equal( - registrationAfter.amountSpent.sub(totalPayment).toString(), - registrationBefore.amountSpent.toString(), - ) - assert.isTrue( - registrationAfter.amountSpent - .sub(registrationBefore.amountSpent) - .eq(registrationBefore.balance.sub(registrationAfter.balance)), - ) - // Last perform block number should be updated - assert.equal( - registrationAfter.lastPerformedBlockNumber.toString(), - tx.blockNumber?.toString(), - ) - - // Latest epoch should be 5 - assert.equal((await registry.getState()).state.latestEpoch, 5) - } - }, - ) - - // skipping it for now as it is passing in local but failing in CI - describe.skip('Gas benchmarking conditional upkeeps [ @skip-coverage ]', function () { - const fs = [1, 10] - fs.forEach(function (newF) { - it( - 'When f=' + - newF + - ' calculates gas overhead appropriately within a margin for different scenarios', - async () => { - // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement - let tx = await getTransmitTx(registry, keeper1, [upkeepId]) - await tx.wait() - - // Different test scenarios - let longBytes = '0x' - for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { - longBytes += '11' - } - const upkeepSuccessArray = [true, false] - const performGasArray = [5000, performGas] - const performDataArray = ['0x', longBytes] - const chainModuleOverheads = - await chainModuleBase.getGasOverhead() - - for (const i in upkeepSuccessArray) { - for (const j in performGasArray) { - for (const k in performDataArray) { - const upkeepSuccess = upkeepSuccessArray[i] - const performGas = performGasArray[j] - const performData = performDataArray[k] - - await mock.setCanPerform(upkeepSuccess) - await mock.setPerformGasToBurn(performGas) - await registry - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - newF, - config, - offchainVersion, - offchainBytes, - ) - tx = await getTransmitTx(registry, keeper1, [upkeepId], { - numSigners: newF + 1, - performDatas: [performData], - }) - const receipt = await tx.wait() - const upkeepPerformedLogs = - parseUpkeepPerformedLogs(receipt) - // exactly 1 Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, 1) - const upkeepPerformedLog = upkeepPerformedLogs[0] - - const upkeepGasUsed = upkeepPerformedLog.args.gasUsed - const chargedGasOverhead = - upkeepPerformedLog.args.gasOverhead - const actualGasOverhead = receipt.gasUsed.sub(upkeepGasUsed) - const estimatedGasOverhead = registryConditionalOverhead - .add( - registryPerSignerGasOverhead.mul( - BigNumber.from(newF + 1), - ), - ) - .add( - registryPerPerformByteGasOverhead - .add(chainModuleOverheads.chainModulePerByteOverhead) - .mul( - BigNumber.from(performData.length / 2 - 1) - .add(registryTransmitCalldataFixedBytesOverhead) - .add( - registryTransmitCalldataPerSignerBytesOverhead.mul( - BigNumber.from(newF + 1), - ), - ), - ), - ) - .add(chainModuleOverheads.chainModuleFixedOverhead) - - assert.isTrue(upkeepGasUsed.gt(BigNumber.from('0'))) - assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) - assert.isTrue(actualGasOverhead.gt(BigNumber.from('0'))) - - console.log( - 'Gas Benchmarking conditional upkeeps:', - 'upkeepSuccess=', - upkeepSuccess, - 'performGas=', - performGas.toString(), - 'performData length=', - performData.length / 2 - 1, - 'sig verification ( f =', - newF, - '): estimated overhead: ', - estimatedGasOverhead.toString(), - ' charged overhead: ', - chargedGasOverhead.toString(), - ' actual overhead: ', - actualGasOverhead.toString(), - ' calculation margin over gasUsed: ', - chargedGasOverhead.sub(actualGasOverhead).toString(), - ' estimation margin over gasUsed: ', - estimatedGasOverhead.sub(actualGasOverhead).toString(), - ) - - // The actual gas overhead should be less than charged gas overhead, but not by a lot - // The charged gas overhead is controlled by ACCOUNTING_FIXED_GAS_OVERHEAD and - // ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD, and their correct values should be set to - // satisfy constraints in multiple places - assert.isTrue( - chargedGasOverhead.gt(actualGasOverhead), - 'Gas overhead calculated is too low, increase account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD) by at least ' + - actualGasOverhead.sub(chargedGasOverhead).toString(), - ) - assert.isTrue( - chargedGasOverhead - .sub(actualGasOverhead) - .lt(gasCalculationMargin), - 'Gas overhead calculated is too high, decrease account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by at least ' + - chargedGasOverhead - .sub(actualGasOverhead) - .sub(gasCalculationMargin) - .toString(), - ) - - // The estimated overhead during checkUpkeep should be close to the actual overhead in transaction - // It should be greater than the actual overhead but not by a lot - // The estimated overhead is controlled by variables - // REGISTRY_CONDITIONAL_OVERHEAD, REGISTRY_LOG_OVERHEAD, REGISTRY_PER_SIGNER_GAS_OVERHEAD - // REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD - assert.isTrue( - estimatedGasOverhead.gt(actualGasOverhead), - 'Gas overhead estimated in check upkeep is too low, increase estimation gas variables (REGISTRY_CONDITIONAL_OVERHEAD/REGISTRY_LOG_OVERHEAD/REGISTRY_PER_SIGNER_GAS_OVERHEAD/REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD) by at least ' + - estimatedGasOverhead.sub(chargedGasOverhead).toString(), - ) - assert.isTrue( - estimatedGasOverhead - .sub(actualGasOverhead) - .lt(gasEstimationMargin), - 'Gas overhead estimated is too high, decrease estimation gas variables (REGISTRY_CONDITIONAL_OVERHEAD/REGISTRY_LOG_OVERHEAD/REGISTRY_PER_SIGNER_GAS_OVERHEAD/REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD) by at least ' + - estimatedGasOverhead - .sub(actualGasOverhead) - .sub(gasEstimationMargin) - .toString(), - ) - } - } - } - }, - ) - }) - }) - - describe('Gas benchmarking log upkeeps [ @skip-coverage ]', function () { - const fs = [1, 10] - fs.forEach(function (newF) { - it( - 'When f=' + - newF + - ' calculates gas overhead appropriately within a margin', - async () => { - // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement - let tx = await getTransmitTx(registry, keeper1, [logUpkeepId]) - await tx.wait() - const performData = '0x' - await mock.setCanPerform(true) - await mock.setPerformGasToBurn(performGas) - await registry.setConfigTypeSafe( - signerAddresses, - keeperAddresses, - newF, - config, - offchainVersion, - offchainBytes, - ) - tx = await getTransmitTx(registry, keeper1, [logUpkeepId], { - numSigners: newF + 1, - performDatas: [performData], - }) - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly 1 Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, 1) - const upkeepPerformedLog = upkeepPerformedLogs[0] - const chainModuleOverheads = - await chainModuleBase.getGasOverhead() - - const upkeepGasUsed = upkeepPerformedLog.args.gasUsed - const chargedGasOverhead = upkeepPerformedLog.args.gasOverhead - const actualGasOverhead = receipt.gasUsed.sub(upkeepGasUsed) - const estimatedGasOverhead = registryLogOverhead - .add(registryPerSignerGasOverhead.mul(BigNumber.from(newF + 1))) - .add( - registryPerPerformByteGasOverhead - .add(chainModuleOverheads.chainModulePerByteOverhead) - .mul( - BigNumber.from(performData.length / 2 - 1) - .add(registryTransmitCalldataFixedBytesOverhead) - .add( - registryTransmitCalldataPerSignerBytesOverhead.mul( - BigNumber.from(newF + 1), - ), - ), - ), - ) - .add(chainModuleOverheads.chainModuleFixedOverhead) - - assert.isTrue(upkeepGasUsed.gt(BigNumber.from('0'))) - assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) - assert.isTrue(actualGasOverhead.gt(BigNumber.from('0'))) - - console.log( - 'Gas Benchmarking log upkeeps:', - 'upkeepSuccess=', - true, - 'performGas=', - performGas.toString(), - 'performData length=', - performData.length / 2 - 1, - 'sig verification ( f =', - newF, - '): estimated overhead: ', - estimatedGasOverhead.toString(), - ' charged overhead: ', - chargedGasOverhead.toString(), - ' actual overhead: ', - actualGasOverhead.toString(), - ' calculation margin over gasUsed: ', - chargedGasOverhead.sub(actualGasOverhead).toString(), - ' estimation margin over gasUsed: ', - estimatedGasOverhead.sub(actualGasOverhead).toString(), - ) - - assert.isTrue( - chargedGasOverhead.gt(actualGasOverhead), - 'Gas overhead calculated is too low, increase account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD) by at least ' + - actualGasOverhead.sub(chargedGasOverhead).toString(), - ) - assert.isTrue( - chargedGasOverhead - .sub(actualGasOverhead) - .lt(gasCalculationMargin), - 'Gas overhead calculated is too high, decrease account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by at least ' + - chargedGasOverhead - .sub(actualGasOverhead) - .sub(gasCalculationMargin) - .toString(), - ) - - assert.isTrue( - estimatedGasOverhead.gt(actualGasOverhead), - 'Gas overhead estimated in check upkeep is too low, increase estimation gas variables (REGISTRY_CONDITIONAL_OVERHEAD/REGISTRY_LOG_OVERHEAD/REGISTRY_PER_SIGNER_GAS_OVERHEAD/REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD) by at least ' + - estimatedGasOverhead.sub(chargedGasOverhead).toString(), - ) - assert.isTrue( - estimatedGasOverhead - .sub(actualGasOverhead) - .lt(gasEstimationMargin), - 'Gas overhead estimated is too high, decrease estimation gas variables (REGISTRY_CONDITIONAL_OVERHEAD/REGISTRY_LOG_OVERHEAD/REGISTRY_PER_SIGNER_GAS_OVERHEAD/REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD) by at least ' + - estimatedGasOverhead - .sub(actualGasOverhead) - .sub(gasEstimationMargin) - .toString(), - ) - }, - ) - }) - }) - }) - }) - - describe('#transmit with upkeep batches [ @skip-coverage ]', function () { - const numPassingConditionalUpkeepsArray = [0, 1, 5] - const numPassingLogUpkeepsArray = [0, 1, 5] - const numFailingUpkeepsArray = [0, 3] - - for (let idx = 0; idx < numPassingConditionalUpkeepsArray.length; idx++) { - for (let jdx = 0; jdx < numPassingLogUpkeepsArray.length; jdx++) { - for (let kdx = 0; kdx < numFailingUpkeepsArray.length; kdx++) { - const numPassingConditionalUpkeeps = - numPassingConditionalUpkeepsArray[idx] - const numPassingLogUpkeeps = numPassingLogUpkeepsArray[jdx] - const numFailingUpkeeps = numFailingUpkeepsArray[kdx] - if (numPassingConditionalUpkeeps == 0 && numPassingLogUpkeeps == 0) { - continue - } - it( - '[Conditional:' + - numPassingConditionalUpkeeps + - ',Log:' + - numPassingLogUpkeeps + - ',Failures:' + - numFailingUpkeeps + - '] performs successful upkeeps and does not charge failing upkeeps', - async () => { - const allUpkeeps = await getMultipleUpkeepsDeployedAndFunded( - numPassingConditionalUpkeeps, - numPassingLogUpkeeps, - numFailingUpkeeps, - ) - const passingConditionalUpkeepIds = - allUpkeeps.passingConditionalUpkeepIds - const passingLogUpkeepIds = allUpkeeps.passingLogUpkeepIds - const failingUpkeepIds = allUpkeeps.failingUpkeepIds - - const keeperBefore = await registry.getTransmitterInfo( - await keeper1.getAddress(), - ) - const keeperLinkBefore = await linkToken.balanceOf( - await keeper1.getAddress(), - ) - const registryLinkBefore = await linkToken.balanceOf( - registry.address, - ) - const registryPremiumBefore = (await registry.getState()).state - .totalPremium - const registrationConditionalPassingBefore = await Promise.all( - passingConditionalUpkeepIds.map(async (id) => { - const reg = await registry.getUpkeep(BigNumber.from(id)) - assert.equal(reg.lastPerformedBlockNumber.toString(), '0') - return reg - }), - ) - const registrationLogPassingBefore = await Promise.all( - passingLogUpkeepIds.map(async (id) => { - const reg = await registry.getUpkeep(BigNumber.from(id)) - assert.equal(reg.lastPerformedBlockNumber.toString(), '0') - return reg - }), - ) - const registrationFailingBefore = await Promise.all( - failingUpkeepIds.map(async (id) => { - const reg = await registry.getUpkeep(BigNumber.from(id)) - assert.equal(reg.lastPerformedBlockNumber.toString(), '0') - return reg - }), - ) - - // cancel upkeeps so they will fail in the transmit process - // must call the cancel upkeep as the owner to avoid the CANCELLATION_DELAY - for (let ldx = 0; ldx < failingUpkeepIds.length; ldx++) { - await registry - .connect(owner) - .cancelUpkeep(failingUpkeepIds[ldx]) - } - - const tx = await getTransmitTx( - registry, - keeper1, - passingConditionalUpkeepIds.concat( - passingLogUpkeepIds.concat(failingUpkeepIds), - ), - ) - - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly numPassingUpkeeps Upkeep Performed should be emitted - assert.equal( - upkeepPerformedLogs.length, - numPassingConditionalUpkeeps + numPassingLogUpkeeps, - ) - const cancelledUpkeepReportLogs = - parseCancelledUpkeepReportLogs(receipt) - // exactly numFailingUpkeeps Upkeep Performed should be emitted - assert.equal(cancelledUpkeepReportLogs.length, numFailingUpkeeps) - - const keeperAfter = await registry.getTransmitterInfo( - await keeper1.getAddress(), - ) - const keeperLinkAfter = await linkToken.balanceOf( - await keeper1.getAddress(), - ) - const registryLinkAfter = await linkToken.balanceOf( - registry.address, - ) - const registrationConditionalPassingAfter = await Promise.all( - passingConditionalUpkeepIds.map(async (id) => { - return await registry.getUpkeep(BigNumber.from(id)) - }), - ) - const registrationLogPassingAfter = await Promise.all( - passingLogUpkeepIds.map(async (id) => { - return await registry.getUpkeep(BigNumber.from(id)) - }), - ) - const registrationFailingAfter = await Promise.all( - failingUpkeepIds.map(async (id) => { - return await registry.getUpkeep(BigNumber.from(id)) - }), - ) - const registryPremiumAfter = (await registry.getState()).state - .totalPremium - const premium = registryPremiumAfter.sub(registryPremiumBefore) - - let netPayment = BigNumber.from('0') - for (let i = 0; i < numPassingConditionalUpkeeps; i++) { - const id = upkeepPerformedLogs[i].args.id - const gasUsed = upkeepPerformedLogs[i].args.gasUsed - const gasOverhead = upkeepPerformedLogs[i].args.gasOverhead - const totalPayment = upkeepPerformedLogs[i].args.totalPayment - - expect(id).to.equal(passingConditionalUpkeepIds[i]) - assert.isTrue(gasUsed.gt(BigNumber.from('0'))) - assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) - assert.isTrue(totalPayment.gt(BigNumber.from('0'))) - - // Balance should be deducted - assert.equal( - registrationConditionalPassingBefore[i].balance - .sub(totalPayment) - .toString(), - registrationConditionalPassingAfter[i].balance.toString(), - ) - - // Amount spent should be updated correctly - assert.equal( - registrationConditionalPassingAfter[i].amountSpent - .sub(totalPayment) - .toString(), - registrationConditionalPassingBefore[ - i - ].amountSpent.toString(), - ) - - // Last perform block number should be updated - assert.equal( - registrationConditionalPassingAfter[ - i - ].lastPerformedBlockNumber.toString(), - tx.blockNumber?.toString(), - ) - - netPayment = netPayment.add(totalPayment) - } - - for (let i = 0; i < numPassingLogUpkeeps; i++) { - const id = - upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args.id - const gasUsed = - upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args - .gasUsed - const gasOverhead = - upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args - .gasOverhead - const totalPayment = - upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args - .totalPayment - - expect(id).to.equal(passingLogUpkeepIds[i]) - assert.isTrue(gasUsed.gt(BigNumber.from('0'))) - assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) - assert.isTrue(totalPayment.gt(BigNumber.from('0'))) - - // Balance should be deducted - assert.equal( - registrationLogPassingBefore[i].balance - .sub(totalPayment) - .toString(), - registrationLogPassingAfter[i].balance.toString(), - ) - - // Amount spent should be updated correctly - assert.equal( - registrationLogPassingAfter[i].amountSpent - .sub(totalPayment) - .toString(), - registrationLogPassingBefore[i].amountSpent.toString(), - ) - - // Last perform block number should not be updated for log triggers - assert.equal( - registrationLogPassingAfter[ - i - ].lastPerformedBlockNumber.toString(), - '0', - ) - - netPayment = netPayment.add(totalPayment) - } - - for (let i = 0; i < numFailingUpkeeps; i++) { - // CancelledUpkeep log should be emitted - const id = cancelledUpkeepReportLogs[i].args.id - expect(id).to.equal(failingUpkeepIds[i]) - - // Balance and amount spent should be same - assert.equal( - registrationFailingBefore[i].balance.toString(), - registrationFailingAfter[i].balance.toString(), - ) - assert.equal( - registrationFailingBefore[i].amountSpent.toString(), - registrationFailingAfter[i].amountSpent.toString(), - ) - - // Last perform block number should not be updated - assert.equal( - registrationFailingAfter[ - i - ].lastPerformedBlockNumber.toString(), - '0', - ) - } - - // Keeper payment is gasPayment + premium / num keepers - const keeperPayment = netPayment - .sub(premium) - .add(premium.div(BigNumber.from(keeperAddresses.length))) - - // Keeper should be paid net payment for all passed upkeeps - assert.equal( - keeperAfter.balance.sub(keeperPayment).toString(), - keeperBefore.balance.toString(), - ) - - assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) - assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) - }, - ) - - it( - '[Conditional:' + - numPassingConditionalUpkeeps + - ',Log' + - numPassingLogUpkeeps + - ',Failures:' + - numFailingUpkeeps + - '] splits gas overhead appropriately among performed upkeeps [ @skip-coverage ]', - async () => { - const allUpkeeps = await getMultipleUpkeepsDeployedAndFunded( - numPassingConditionalUpkeeps, - numPassingLogUpkeeps, - numFailingUpkeeps, - ) - const passingConditionalUpkeepIds = - allUpkeeps.passingConditionalUpkeepIds - const passingLogUpkeepIds = allUpkeeps.passingLogUpkeepIds - const failingUpkeepIds = allUpkeeps.failingUpkeepIds - - // Perform the upkeeps once to remove non-zero storage slots and have predictable gas measurement - let tx = await getTransmitTx( - registry, - keeper1, - passingConditionalUpkeepIds.concat( - passingLogUpkeepIds.concat(failingUpkeepIds), - ), - ) - - await tx.wait() - - // cancel upkeeps so they will fail in the transmit process - // must call the cancel upkeep as the owner to avoid the CANCELLATION_DELAY - for (let ldx = 0; ldx < failingUpkeepIds.length; ldx++) { - await registry - .connect(owner) - .cancelUpkeep(failingUpkeepIds[ldx]) - } - - // Do the actual thing - - tx = await getTransmitTx( - registry, - keeper1, - passingConditionalUpkeepIds.concat( - passingLogUpkeepIds.concat(failingUpkeepIds), - ), - ) - - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly numPassingUpkeeps Upkeep Performed should be emitted - assert.equal( - upkeepPerformedLogs.length, - numPassingConditionalUpkeeps + numPassingLogUpkeeps, - ) - - let netGasUsedPlusChargedOverhead = BigNumber.from('0') - for (let i = 0; i < numPassingConditionalUpkeeps; i++) { - const gasUsed = upkeepPerformedLogs[i].args.gasUsed - const chargedGasOverhead = - upkeepPerformedLogs[i].args.gasOverhead - - assert.isTrue(gasUsed.gt(BigNumber.from('0'))) - assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) - - // Overhead should be same for every upkeep - assert.isTrue( - chargedGasOverhead.eq( - upkeepPerformedLogs[0].args.gasOverhead, - ), - ) - netGasUsedPlusChargedOverhead = netGasUsedPlusChargedOverhead - .add(gasUsed) - .add(chargedGasOverhead) - } - - for (let i = 0; i < numPassingLogUpkeeps; i++) { - const gasUsed = - upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args - .gasUsed - const chargedGasOverhead = - upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args - .gasOverhead - - assert.isTrue(gasUsed.gt(BigNumber.from('0'))) - assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) - - // Overhead should be same for every upkeep - assert.isTrue( - chargedGasOverhead.eq( - upkeepPerformedLogs[numPassingConditionalUpkeeps].args - .gasOverhead, - ), - ) - netGasUsedPlusChargedOverhead = netGasUsedPlusChargedOverhead - .add(gasUsed) - .add(chargedGasOverhead) - } - - console.log( - 'Gas Benchmarking - batching (passedConditionalUpkeeps: ', - numPassingConditionalUpkeeps, - 'passedLogUpkeeps:', - numPassingLogUpkeeps, - 'failedUpkeeps:', - numFailingUpkeeps, - '): ', - numPassingConditionalUpkeeps > 0 - ? 'charged conditional overhead' - : '', - numPassingConditionalUpkeeps > 0 - ? upkeepPerformedLogs[0].args.gasOverhead.toString() - : '', - numPassingLogUpkeeps > 0 ? 'charged log overhead' : '', - numPassingLogUpkeeps > 0 - ? upkeepPerformedLogs[ - numPassingConditionalUpkeeps - ].args.gasOverhead.toString() - : '', - ' margin over gasUsed', - netGasUsedPlusChargedOverhead.sub(receipt.gasUsed).toString(), - ) - - // The total gas charged should be greater than tx gas - assert.isTrue( - netGasUsedPlusChargedOverhead.gt(receipt.gasUsed), - 'Charged gas overhead is too low for batch upkeeps, increase ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD', - ) - }, - ) - } - } - } - - it('has enough perform gas overhead for large batches [ @skip-coverage ]', async () => { - const numUpkeeps = 20 - const upkeepIds: BigNumber[] = [] - let totalPerformGas = BigNumber.from('0') - for (let i = 0; i < numUpkeeps; i++) { - const mock = await upkeepMockFactory.deploy() - const tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') - const testUpkeepId = await getUpkeepID(tx) - upkeepIds.push(testUpkeepId) - - // Add funds to passing upkeeps - await registry.connect(owner).addFunds(testUpkeepId, toWei('10')) - - await mock.setCanPerform(true) - await mock.setPerformGasToBurn(performGas) - - totalPerformGas = totalPerformGas.add(performGas) - } - - // Should revert with no overhead added - await evmRevert( - getTransmitTx(registry, keeper1, upkeepIds, { - gasLimit: totalPerformGas, - }), - ) - // Should not revert with overhead added - await getTransmitTx(registry, keeper1, upkeepIds, { - gasLimit: totalPerformGas.add(transmitGasOverhead), - }) - }) - - it('splits l2 payment among performed upkeeps according to perform data weight', async () => { - const numUpkeeps = 7 - const upkeepIds: BigNumber[] = [] - const performDataSizes = [0, 10, 1000, 50, 33, 69, 420] - const performDatas: string[] = [] - const upkeepCalldataWeights: BigNumber[] = [] - let totalCalldataWeight = BigNumber.from('0') - // Same as MockArbGasInfo.sol - const l1CostWeiArb = BigNumber.from(1000000) - - for (let i = 0; i < numUpkeeps; i++) { - const mock = await upkeepMockFactory.deploy() - const tx = await arbRegistry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') - const testUpkeepId = await getUpkeepID(tx) - upkeepIds.push(testUpkeepId) - - // Add funds to passing upkeeps - await arbRegistry.connect(owner).addFunds(testUpkeepId, toWei('100')) - - // Generate performData - let pd = '0x' - for (let j = 0; j < performDataSizes[i]; j++) { - pd += '11' - } - performDatas.push(pd) - const w = BigNumber.from(performDataSizes[i]) - .add(registryTransmitCalldataFixedBytesOverhead) - .add( - registryTransmitCalldataPerSignerBytesOverhead.mul( - BigNumber.from(f + 1), - ), - ) - upkeepCalldataWeights.push(w) - totalCalldataWeight = totalCalldataWeight.add(w) - } - - // Do the thing - const tx = await getTransmitTx(arbRegistry, keeper1, upkeepIds, { - gasPrice: gasWei.mul('5'), // High gas price so that it gets capped - performDatas, - }) - - const receipt = await tx.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - // exactly numPassingUpkeeps Upkeep Performed should be emitted - assert.equal(upkeepPerformedLogs.length, numUpkeeps) - - for (let i = 0; i < numUpkeeps; i++) { - const upkeepPerformedLog = upkeepPerformedLogs[i] - - const gasUsed = upkeepPerformedLog.args.gasUsed - const gasOverhead = upkeepPerformedLog.args.gasOverhead - const totalPayment = upkeepPerformedLog.args.totalPayment - - assert.equal( - linkForGas( - gasUsed, - gasOverhead, - gasCeilingMultiplier, - paymentPremiumPPB, - flatFeeMicroLink, - l1CostWeiArb.mul(upkeepCalldataWeights[i]).div(totalCalldataWeight), - ).total.toString(), - totalPayment.toString(), - ) - } - }) - }) - - describe('#recoverFunds', () => { - const sent = toWei('7') - - beforeEach(async () => { - await linkToken.connect(admin).approve(registry.address, toWei('100')) - await linkToken - .connect(owner) - .transfer(await keeper1.getAddress(), toWei('1000')) - - // add funds to upkeep 1 and perform and withdraw some payment - const tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), emptyBytes, emptyBytes) - - const id1 = await getUpkeepID(tx) - await registry.connect(admin).addFunds(id1, toWei('5')) - - await getTransmitTx(registry, keeper1, [id1]) - await getTransmitTx(registry, keeper2, [id1]) - await getTransmitTx(registry, keeper3, [id1]) - - 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(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), emptyBytes, emptyBytes) - const id2 = await getUpkeepID(tx2) - await registry.connect(admin).addFunds(id2, toWei('5')) - - await getTransmitTx(registry, keeper1, [id2]) - await getTransmitTx(registry, keeper2, [id2]) - await getTransmitTx(registry, keeper3, [id2]) - - await registry - .connect(payee2) - .withdrawPayment( - await keeper2.getAddress(), - await nonkeeper.getAddress(), - ) - - // transfer funds using onTokenTransfer - const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [id2]) - await linkToken - .connect(owner) - .transferAndCall(registry.address, toWei('1'), data) - - // withdraw some funds - await registry.connect(owner).cancelUpkeep(id1) - await registry - .connect(admin) - .withdrawFunds(id1, await nonkeeper.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) - const ownerBefore = await linkToken.balanceOf(await owner.getAddress()) - - await registry.connect(owner).recoverFunds() - - const balanceAfter = await linkToken.balanceOf(registry.address) - const ownerAfter = await linkToken.balanceOf(await owner.getAddress()) - - assert.isTrue(balanceBefore.eq(balanceAfter.add(sent))) - assert.isTrue(ownerAfter.eq(ownerBefore.add(sent))) - }) - }) - - describe('#getMinBalanceForUpkeep / #checkUpkeep / #transmit', () => { - it('calculates the minimum balance appropriately', async () => { - await mock.setCanCheck(true) - - const oneWei = BigNumber.from(1) - const minBalance = await registry.getMinBalanceForUpkeep(upkeepId) - const tooLow = minBalance.sub(oneWei) - - await registry.connect(admin).addFunds(upkeepId, tooLow) - let checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.INSUFFICIENT_BALANCE, - ) - - await registry.connect(admin).addFunds(upkeepId, oneWei) - checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - assert.equal(checkUpkeepResult.upkeepNeeded, true) - }) - - it('uses maxPerformData size in checkUpkeep but actual performDataSize in transmit', async () => { - const tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') - const upkeepID = await getUpkeepID(tx) - await mock.setCanCheck(true) - await mock.setCanPerform(true) - - // upkeep is underfunded by 1 wei - const minBalance1 = (await registry.getMinBalanceForUpkeep(upkeepID)).sub( - 1, - ) - await registry.connect(owner).addFunds(upkeepID, minBalance1) - - // upkeep check should return false, 2 should return true - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepID) - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.INSUFFICIENT_BALANCE, - ) - - // however upkeep should perform and pay all the remaining balance - let maxPerformData = '0x' - for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { - maxPerformData += '11' - } - - const tx2 = await getTransmitTx(registry, keeper1, [upkeepID], { - gasPrice: gasWei.mul(gasCeilingMultiplier), - performDatas: [maxPerformData], - }) - - const receipt = await tx2.wait() - const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) - assert.equal(upkeepPerformedLogs.length, 1) - }) - }) - - describe('#withdrawFunds', () => { - let upkeepId2: BigNumber - - beforeEach(async () => { - const tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), randomBytes, '0x') - upkeepId2 = await getUpkeepID(tx) - - await registry.connect(admin).addFunds(upkeepId, toWei('100')) - await registry.connect(admin).addFunds(upkeepId2, toWei('100')) - - // Do a perform so that upkeep is charged some amount - await getTransmitTx(registry, keeper1, [upkeepId]) - await getTransmitTx(registry, keeper1, [upkeepId2]) - }) - - it('reverts if called on a non existing ID', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .withdrawFunds(upkeepId.add(1), await payee1.getAddress()), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevertCustomError( - registry - .connect(owner) - .withdrawFunds(upkeepId, await payee1.getAddress()), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if called on an uncanceled upkeep', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .withdrawFunds(upkeepId, await payee1.getAddress()), - registry, - 'UpkeepNotCanceled', - ) - }) - - it('reverts if called with the 0 address', async () => { - await evmRevertCustomError( - registry.connect(admin).withdrawFunds(upkeepId, zeroAddress), - registry, - 'InvalidRecipient', - ) - }) - - describe('after the registration is paused, then cancelled', () => { - it('allows the admin to withdraw', async () => { - const balance = await registry.getBalance(upkeepId) - const payee = await payee1.getAddress() - await registry.connect(admin).pauseUpkeep(upkeepId) - await registry.connect(owner).cancelUpkeep(upkeepId) - await expect(() => - registry.connect(admin).withdrawFunds(upkeepId, payee), - ).to.changeTokenBalance(linkToken, payee1, balance) - }) - }) - - describe('after the registration is cancelled', () => { - beforeEach(async () => { - await registry.connect(owner).cancelUpkeep(upkeepId) - await registry.connect(owner).cancelUpkeep(upkeepId2) - }) - - it('can be called successively on two upkeeps', async () => { - await registry - .connect(admin) - .withdrawFunds(upkeepId, await payee1.getAddress()) - await registry - .connect(admin) - .withdrawFunds(upkeepId2, await payee1.getAddress()) - }) - - it('moves the funds out and updates the balance and emits an event', async () => { - const payee1Before = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const registryBefore = await linkToken.balanceOf(registry.address) - - let registration = await registry.getUpkeep(upkeepId) - const previousBalance = registration.balance - - const tx = await registry - .connect(admin) - .withdrawFunds(upkeepId, await payee1.getAddress()) - await expect(tx) - .to.emit(registry, 'FundsWithdrawn') - .withArgs(upkeepId, previousBalance, await payee1.getAddress()) - - const payee1After = await linkToken.balanceOf(await payee1.getAddress()) - const registryAfter = await linkToken.balanceOf(registry.address) - - assert.isTrue(payee1Before.add(previousBalance).eq(payee1After)) - assert.isTrue(registryBefore.sub(previousBalance).eq(registryAfter)) - - registration = await registry.getUpkeep(upkeepId) - assert.equal(0, registration.balance.toNumber()) - }) - }) - }) - - describe('#simulatePerformUpkeep', () => { - it('reverts if called by non zero address', async () => { - await evmRevertCustomError( - registry - .connect(await owner.getAddress()) - .callStatic.simulatePerformUpkeep(upkeepId, '0x'), - registry, - 'OnlySimulatedBackend', - ) - }) - - it('reverts when registry is paused', async () => { - await registry.connect(owner).pause() - await evmRevertCustomError( - registry - .connect(zeroAddress) - .callStatic.simulatePerformUpkeep(upkeepId, '0x'), - registry, - 'RegistryPaused', - ) - }) - - it('returns false and gasUsed when perform fails', async () => { - await mock.setCanPerform(false) - - const simulatePerformResult = await registry - .connect(zeroAddress) - .callStatic.simulatePerformUpkeep(upkeepId, '0x') - - assert.equal(simulatePerformResult.success, false) - assert.isTrue(simulatePerformResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - }) - - it('returns true, gasUsed, and performGas when perform succeeds', async () => { - await mock.setCanPerform(true) - - const simulatePerformResult = await registry - .connect(zeroAddress) - .callStatic.simulatePerformUpkeep(upkeepId, '0x') - - assert.equal(simulatePerformResult.success, true) - assert.isTrue(simulatePerformResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - }) - - it('returns correct amount of gasUsed when perform succeeds', async () => { - await mock.setCanPerform(true) - await mock.setPerformGasToBurn(performGas) - - const simulatePerformResult = await registry - .connect(zeroAddress) - .callStatic.simulatePerformUpkeep(upkeepId, '0x') - - assert.equal(simulatePerformResult.success, true) - // Full execute gas should be used, with some performGasBuffer(1000) - assert.isTrue( - simulatePerformResult.gasUsed.gt( - performGas.sub(BigNumber.from('1000')), - ), - ) - }) - }) - - describe('#checkUpkeep', () => { - it('reverts if called by non zero address', async () => { - await evmRevertCustomError( - registry - .connect(await owner.getAddress()) - .callStatic['checkUpkeep(uint256)'](upkeepId), - registry, - 'OnlySimulatedBackend', - ) - }) - - it('returns false and error code if the upkeep is cancelled by admin', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal(checkUpkeepResult.performData, '0x') - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.UPKEEP_CANCELLED, - ) - expect(checkUpkeepResult.gasUsed).to.equal(0) - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - }) - - it('returns false and error code if the upkeep is cancelled by owner', async () => { - await registry.connect(owner).cancelUpkeep(upkeepId) - - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal(checkUpkeepResult.performData, '0x') - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.UPKEEP_CANCELLED, - ) - expect(checkUpkeepResult.gasUsed).to.equal(0) - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - }) - - it('returns false and error code if the registry is paused', async () => { - await registry.connect(owner).pause() - - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal(checkUpkeepResult.performData, '0x') - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.REGISTRY_PAUSED, - ) - expect(checkUpkeepResult.gasUsed).to.equal(0) - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - }) - - it('returns false and error code if the upkeep is paused', async () => { - await registry.connect(admin).pauseUpkeep(upkeepId) - - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal(checkUpkeepResult.performData, '0x') - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.UPKEEP_PAUSED, - ) - expect(checkUpkeepResult.gasUsed).to.equal(0) - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - }) - - it('returns false and error code if user is out of funds', async () => { - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal(checkUpkeepResult.performData, '0x') - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.INSUFFICIENT_BALANCE, - ) - expect(checkUpkeepResult.gasUsed).to.equal(0) - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - }) - - context('when the registration is funded', () => { - beforeEach(async () => { - await linkToken.connect(admin).approve(registry.address, toWei('200')) - await registry.connect(admin).addFunds(upkeepId, toWei('100')) - await registry.connect(admin).addFunds(logUpkeepId, toWei('100')) - }) - - it('returns false, error code, and revert data if the target check reverts', async () => { - await mock.setShouldRevertCheck(true) - await mock.setCheckRevertReason( - 'custom revert error, clever way to insert offchain data', - ) - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - assert.equal(checkUpkeepResult.upkeepNeeded, false) - - const revertReasonBytes = `0x${checkUpkeepResult.performData.slice(10)}` // remove sighash - assert.equal( - ethers.utils.defaultAbiCoder.decode(['string'], revertReasonBytes)[0], - 'custom revert error, clever way to insert offchain data', - ) - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.TARGET_CHECK_REVERTED, - ) - assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - // Feed data should be returned here - assert.isTrue(checkUpkeepResult.fastGasWei.gt(BigNumber.from('0'))) - assert.isTrue(checkUpkeepResult.linkNative.gt(BigNumber.from('0'))) - }) - - it('returns false, error code, and no revert data if the target check revert data exceeds maxRevertDataSize', async () => { - await mock.setShouldRevertCheck(true) - let longRevertReason = '' - for (let i = 0; i <= maxRevertDataSize.toNumber(); i++) { - longRevertReason += 'x' - } - await mock.setCheckRevertReason(longRevertReason) - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - assert.equal(checkUpkeepResult.upkeepNeeded, false) - - assert.equal(checkUpkeepResult.performData, '0x') - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.REVERT_DATA_EXCEEDS_LIMIT, - ) - assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - }) - - it('returns false and error code if the upkeep is not needed', async () => { - await mock.setCanCheck(false) - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal(checkUpkeepResult.performData, '0x') - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.UPKEEP_NOT_NEEDED, - ) - assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - }) - - it('returns false and error code if the performData exceeds limit', async () => { - let longBytes = '0x' - for (let i = 0; i < 5000; i++) { - longBytes += '1' - } - await mock.setCanCheck(true) - await mock.setPerformData(longBytes) - - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId) - - assert.equal(checkUpkeepResult.upkeepNeeded, false) - assert.equal(checkUpkeepResult.performData, '0x') - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, - ) - assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - }) - - it('returns true with gas used if the target can execute', async () => { - await mock.setCanCheck(true) - await mock.setPerformData(randomBytes) - - const latestBlock = await ethers.provider.getBlock('latest') - - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId, { - blockTag: latestBlock.number, - }) - - assert.equal(checkUpkeepResult.upkeepNeeded, true) - assert.equal(checkUpkeepResult.performData, randomBytes) - assert.equal( - checkUpkeepResult.upkeepFailureReason, - UpkeepFailureReason.NONE, - ) - assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - expect(checkUpkeepResult.gasLimit).to.equal(performGas) - assert.isTrue(checkUpkeepResult.fastGasWei.eq(gasWei)) - assert.isTrue(checkUpkeepResult.linkNative.eq(linkEth)) - }) - - it('calls checkLog for log-trigger upkeeps', async () => { - const log: Log = { - index: 0, - timestamp: 0, - txHash: ethers.utils.randomBytes(32), - blockNumber: 100, - blockHash: ethers.utils.randomBytes(32), - source: randomAddress(), - topics: [ethers.utils.randomBytes(32), ethers.utils.randomBytes(32)], - data: ethers.utils.randomBytes(1000), - } - - await ltUpkeep.mock.checkLog.withArgs(log, '0x').returns(true, '0x1234') - - const checkData = encodeLog(log) - - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256,bytes)'](logUpkeepId, checkData) - - expect(checkUpkeepResult.upkeepNeeded).to.be.true - expect(checkUpkeepResult.performData).to.equal('0x1234') - }) - - itMaybe( - 'has a large enough gas overhead to cover upkeeps that use all their gas [ @skip-coverage ]', - async () => { - await mock.setCanCheck(true) - await mock.setCheckGasToBurn(checkGasLimit) - const gas = checkGasLimit.add(checkGasOverhead) - const checkUpkeepResult = await registry - .connect(zeroAddress) - .callStatic['checkUpkeep(uint256)'](upkeepId, { - gasLimit: gas, - }) - - assert.equal(checkUpkeepResult.upkeepNeeded, true) - }, - ) - }) - }) - - describe('#addFunds', () => { - const amount = toWei('1') - - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry.connect(keeper1).addFunds(upkeepId.add(1), amount), - registry, - 'UpkeepCancelled', - ) - }) - - it('adds to the balance of the registration', async () => { - await registry.connect(admin).addFunds(upkeepId, amount) - const registration = await registry.getUpkeep(upkeepId) - assert.isTrue(amount.eq(registration.balance)) - }) - - it('lets anyone add funds to an upkeep not just admin', async () => { - await linkToken.connect(owner).transfer(await payee1.getAddress(), amount) - await linkToken.connect(payee1).approve(registry.address, amount) - - await registry.connect(payee1).addFunds(upkeepId, amount) - const registration = await registry.getUpkeep(upkeepId) - assert.isTrue(amount.eq(registration.balance)) - }) - - it('emits a log', async () => { - const tx = await registry.connect(admin).addFunds(upkeepId, amount) - await expect(tx) - .to.emit(registry, 'FundsAdded') - .withArgs(upkeepId, await admin.getAddress(), amount) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(keeper1).addFunds(upkeepId, amount), - registry, - 'UpkeepCancelled', - ) - }) - }) - - describe('#getActiveUpkeepIDs', () => { - it('reverts if startIndex is out of bounds ', async () => { - await evmRevertCustomError( - registry.getActiveUpkeepIDs(numUpkeeps, 0), - registry, - 'IndexOutOfRange', - ) - await evmRevertCustomError( - registry.getActiveUpkeepIDs(numUpkeeps + 1, 0), - registry, - 'IndexOutOfRange', - ) - }) - - it('returns upkeep IDs bounded by maxCount', async () => { - let upkeepIds = await registry.getActiveUpkeepIDs(0, 1) - assert(upkeepIds.length == 1) - assert(upkeepIds[0].eq(upkeepId)) - upkeepIds = await registry.getActiveUpkeepIDs(1, 3) - assert(upkeepIds.length == 3) - expect(upkeepIds).to.deep.equal([ - afUpkeepId, - logUpkeepId, - streamsLookupUpkeepId, - ]) - }) - - it('returns as many ids as possible if maxCount > num available', async () => { - const upkeepIds = await registry.getActiveUpkeepIDs(1, numUpkeeps + 100) - assert(upkeepIds.length == numUpkeeps - 1) - }) - - it('returns all upkeep IDs if maxCount is 0', async () => { - let upkeepIds = await registry.getActiveUpkeepIDs(0, 0) - assert(upkeepIds.length == numUpkeeps) - upkeepIds = await registry.getActiveUpkeepIDs(2, 0) - assert(upkeepIds.length == numUpkeeps - 2) - }) - }) - - describe('#getMaxPaymentForGas', () => { - let maxl1CostWeiArbWithoutMultiplier: BigNumber - let maxl1CostWeiOptWithoutMultiplier: BigNumber - - beforeEach(async () => { - const arbL1PriceinWei = BigNumber.from(1000) // Same as MockArbGasInfo.sol - maxl1CostWeiArbWithoutMultiplier = arbL1PriceinWei.mul( - maxPerformDataSize - .add(registryTransmitCalldataFixedBytesOverhead) - .add( - registryTransmitCalldataPerSignerBytesOverhead.mul( - BigNumber.from(f + 1), - ), - ), - ) - maxl1CostWeiOptWithoutMultiplier = BigNumber.from(2000000) // Same as MockOVMGasPriceOracle.sol - }) - - itMaybe('calculates the max fee appropriately', async () => { - await verifyMaxPayment(registry, chainModuleBase) - }) - - itMaybe('calculates the max fee appropriately for Arbitrum', async () => { - await verifyMaxPayment( - arbRegistry, - arbitrumModule, - maxl1CostWeiArbWithoutMultiplier, - ) - }) - - itMaybe('calculates the max fee appropriately for Optimism', async () => { - await verifyMaxPayment( - opRegistry, - optimismModule, - maxl1CostWeiOptWithoutMultiplier, - ) - }) - - it('uses the fallback gas price if the feed has issues', async () => { - const chainModuleOverheads = await chainModuleBase.getGasOverhead() - const expectedFallbackMaxPayment = linkForGas( - performGas, - registryConditionalOverhead - .add(registryPerSignerGasOverhead.mul(f + 1)) - .add( - maxPerformDataSize - .add(registryTransmitCalldataFixedBytesOverhead) - .add( - registryTransmitCalldataPerSignerBytesOverhead.mul( - BigNumber.from(f + 1), - ), - ) - .mul( - registryPerPerformByteGasOverhead.add( - chainModuleOverheads.chainModulePerByteOverhead, - ), - ), - ) - .add(chainModuleOverheads.chainModuleFixedOverhead), - gasCeilingMultiplier.mul('2'), // fallbackGasPrice is 2x gas price - paymentPremiumPPB, - flatFeeMicroLink, - ).total - - // Stale feed - let roundId = 99 - const answer = 100 - let updatedAt = 946684800 // New Years 2000 🥳 - let startedAt = 946684799 - await gasPriceFeed - .connect(owner) - .updateRoundData(roundId, answer, updatedAt, startedAt) - - assert.equal( - expectedFallbackMaxPayment.toString(), - ( - await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) - ).toString(), - ) - - // Negative feed price - roundId = 100 - updatedAt = now() - startedAt = 946684799 - await gasPriceFeed - .connect(owner) - .updateRoundData(roundId, -100, updatedAt, startedAt) - - assert.equal( - expectedFallbackMaxPayment.toString(), - ( - await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) - ).toString(), - ) - - // Zero feed price - roundId = 101 - updatedAt = now() - startedAt = 946684799 - await gasPriceFeed - .connect(owner) - .updateRoundData(roundId, 0, updatedAt, startedAt) - - assert.equal( - expectedFallbackMaxPayment.toString(), - ( - await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) - ).toString(), - ) - }) - - it('uses the fallback link price if the feed has issues', async () => { - const chainModuleOverheads = await chainModuleBase.getGasOverhead() - const expectedFallbackMaxPayment = linkForGas( - performGas, - registryConditionalOverhead - .add(registryPerSignerGasOverhead.mul(f + 1)) - .add( - maxPerformDataSize - .add(registryTransmitCalldataFixedBytesOverhead) - .add( - registryTransmitCalldataPerSignerBytesOverhead.mul( - BigNumber.from(f + 1), - ), - ) - .mul( - registryPerPerformByteGasOverhead.add( - chainModuleOverheads.chainModulePerByteOverhead, - ), - ), - ) - .add(chainModuleOverheads.chainModuleFixedOverhead), - gasCeilingMultiplier.mul('2'), // fallbackLinkPrice is 1/2 link price, so multiply by 2 - paymentPremiumPPB, - flatFeeMicroLink, - ).total - - // Stale feed - let roundId = 99 - const answer = 100 - let updatedAt = 946684800 // New Years 2000 🥳 - let startedAt = 946684799 - await linkEthFeed - .connect(owner) - .updateRoundData(roundId, answer, updatedAt, startedAt) - - assert.equal( - expectedFallbackMaxPayment.toString(), - ( - await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) - ).toString(), - ) - - // Negative feed price - roundId = 100 - updatedAt = now() - startedAt = 946684799 - await linkEthFeed - .connect(owner) - .updateRoundData(roundId, -100, updatedAt, startedAt) - - assert.equal( - expectedFallbackMaxPayment.toString(), - ( - await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) - ).toString(), - ) - - // Zero feed price - roundId = 101 - updatedAt = now() - startedAt = 946684799 - await linkEthFeed - .connect(owner) - .updateRoundData(roundId, 0, updatedAt, startedAt) - - assert.equal( - expectedFallbackMaxPayment.toString(), - ( - await registry.getMaxPaymentForGas(Trigger.CONDITION, performGas) - ).toString(), - ) - }) - }) - - describe('#typeAndVersion', () => { - it('uses the correct type and version', async () => { - const typeAndVersion = await registry.typeAndVersion() - assert.equal(typeAndVersion, 'AutomationRegistry 2.2.0') - }) - }) - - describe('#onTokenTransfer', () => { - const amount = toWei('1') - - it('reverts if not called by the LINK token', async () => { - const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) - - await evmRevertCustomError( - registry - .connect(keeper1) - .onTokenTransfer(await keeper1.getAddress(), amount, data), - registry, - 'OnlyCallableByLINKToken', - ) - }) - - 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(upkeepId) - await evmRevertCustomError( - registry.connect(keeper1).addFunds(upkeepId, amount), - registry, - 'UpkeepCancelled', - ) - }) - - it('updates the funds of the job id passed', async () => { - const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) - - const before = (await registry.getUpkeep(upkeepId)).balance - await linkToken - .connect(owner) - .transferAndCall(registry.address, amount, data) - const after = (await registry.getUpkeep(upkeepId)).balance - - assert.isTrue(before.add(amount).eq(after)) - }) - }) - - describeMaybe('#setConfig - onchain', async () => { - const payment = BigNumber.from(1) - const flatFee = BigNumber.from(2) - const maxGas = BigNumber.from(6) - const staleness = BigNumber.from(4) - const ceiling = BigNumber.from(5) - const newMinUpkeepSpend = BigNumber.from(9) - const newMaxCheckDataSize = BigNumber.from(10000) - const newMaxPerformDataSize = BigNumber.from(10000) - const newMaxRevertDataSize = BigNumber.from(10000) - const newMaxPerformGas = BigNumber.from(10000000) - const fbGasEth = BigNumber.from(7) - const fbLinkEth = BigNumber.from(8) - const newTranscoder = randomAddress() - const newRegistrars = [randomAddress(), randomAddress()] - const upkeepManager = randomAddress() - - const newConfig = { - paymentPremiumPPB: payment, - flatFeeMicroLink: flatFee, - checkGasLimit: maxGas, - stalenessSeconds: staleness, - gasCeilingMultiplier: ceiling, - minUpkeepSpend: newMinUpkeepSpend, - maxCheckDataSize: newMaxCheckDataSize, - maxPerformDataSize: newMaxPerformDataSize, - maxRevertDataSize: newMaxRevertDataSize, - maxPerformGas: newMaxPerformGas, - fallbackGasPrice: fbGasEth, - fallbackLinkPrice: fbLinkEth, - transcoder: newTranscoder, - registrars: newRegistrars, - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - } - - it('reverts when called by anyone but the proposed owner', async () => { - await evmRevert( - registry - .connect(payee1) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - newConfig, - offchainVersion, - offchainBytes, - ), - 'Only callable by owner', - ) - }) - - it('reverts if signers or transmitters are the zero address', async () => { - await evmRevertCustomError( - registry - .connect(owner) - .setConfigTypeSafe( - [randomAddress(), randomAddress(), randomAddress(), zeroAddress], - [ - randomAddress(), - randomAddress(), - randomAddress(), - randomAddress(), - ], - f, - newConfig, - offchainVersion, - offchainBytes, - ), - registry, - 'InvalidSigner', - ) - - await evmRevertCustomError( - registry - .connect(owner) - .setConfigTypeSafe( - [ - randomAddress(), - randomAddress(), - randomAddress(), - randomAddress(), - ], - [randomAddress(), randomAddress(), randomAddress(), zeroAddress], - f, - newConfig, - offchainVersion, - offchainBytes, - ), - registry, - 'InvalidTransmitter', - ) - }) - - it('updates the onchainConfig and configDigest', async () => { - const old = await registry.getState() - const oldConfig = old.config - const oldState = old.state - assert.isTrue(paymentPremiumPPB.eq(oldConfig.paymentPremiumPPB)) - assert.isTrue(flatFeeMicroLink.eq(oldConfig.flatFeeMicroLink)) - assert.isTrue(stalenessSeconds.eq(oldConfig.stalenessSeconds)) - assert.isTrue(gasCeilingMultiplier.eq(oldConfig.gasCeilingMultiplier)) - - await registry - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - newConfig, - offchainVersion, - offchainBytes, - ) - - const updated = await registry.getState() - const updatedConfig = updated.config - const updatedState = updated.state - assert.equal(updatedConfig.paymentPremiumPPB, payment.toNumber()) - assert.equal(updatedConfig.flatFeeMicroLink, flatFee.toNumber()) - assert.equal(updatedConfig.stalenessSeconds, staleness.toNumber()) - assert.equal(updatedConfig.gasCeilingMultiplier, ceiling.toNumber()) - assert.equal( - updatedConfig.minUpkeepSpend.toString(), - newMinUpkeepSpend.toString(), - ) - assert.equal( - updatedConfig.maxCheckDataSize, - newMaxCheckDataSize.toNumber(), - ) - assert.equal( - updatedConfig.maxPerformDataSize, - newMaxPerformDataSize.toNumber(), - ) - assert.equal( - updatedConfig.maxRevertDataSize, - newMaxRevertDataSize.toNumber(), - ) - assert.equal(updatedConfig.maxPerformGas, newMaxPerformGas.toNumber()) - assert.equal(updatedConfig.checkGasLimit, maxGas.toNumber()) - assert.equal( - updatedConfig.fallbackGasPrice.toNumber(), - fbGasEth.toNumber(), - ) - assert.equal( - updatedConfig.fallbackLinkPrice.toNumber(), - fbLinkEth.toNumber(), - ) - assert.equal(updatedState.latestEpoch, 0) - - assert(oldState.configCount + 1 == updatedState.configCount) - assert( - oldState.latestConfigBlockNumber != - updatedState.latestConfigBlockNumber, - ) - assert(oldState.latestConfigDigest != updatedState.latestConfigDigest) - - assert.equal(updatedConfig.transcoder, newTranscoder) - assert.deepEqual(updatedConfig.registrars, newRegistrars) - assert.equal(updatedConfig.upkeepPrivilegeManager, upkeepManager) - }) - - it('maintains paused state when config is changed', async () => { - await registry.pause() - const old = await registry.getState() - assert.isTrue(old.state.paused) - - await registry - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - newConfig, - offchainVersion, - offchainBytes, - ) - - const updated = await registry.getState() - assert.isTrue(updated.state.paused) - }) - - it('emits an event', async () => { - const tx = await registry - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - newConfig, - offchainVersion, - offchainBytes, - ) - await expect(tx).to.emit(registry, 'ConfigSet') - }) - }) - - describe('#setConfig - offchain', () => { - let newKeepers: string[] - - beforeEach(async () => { - newKeepers = [ - await personas.Eddy.getAddress(), - await personas.Nick.getAddress(), - await personas.Neil.getAddress(), - await personas.Carol.getAddress(), - ] - }) - - it('reverts when called by anyone but the owner', async () => { - await evmRevert( - registry - .connect(payee1) - .setConfigTypeSafe( - newKeepers, - newKeepers, - f, - config, - offchainVersion, - offchainBytes, - ), - 'Only callable by owner', - ) - }) - - it('reverts if too many keeperAddresses set', async () => { - for (let i = 0; i < 40; i++) { - newKeepers.push(randomAddress()) - } - await evmRevertCustomError( - registry - .connect(owner) - .setConfigTypeSafe( - newKeepers, - newKeepers, - f, - config, - offchainVersion, - offchainBytes, - ), - registry, - 'TooManyOracles', - ) - }) - - it('reverts if f=0', async () => { - await evmRevertCustomError( - registry - .connect(owner) - .setConfigTypeSafe( - newKeepers, - newKeepers, - 0, - config, - offchainVersion, - offchainBytes, - ), - registry, - 'IncorrectNumberOfFaultyOracles', - ) - }) - - it('reverts if signers != transmitters length', async () => { - const signers = [randomAddress()] - await evmRevertCustomError( - registry - .connect(owner) - .setConfigTypeSafe( - signers, - newKeepers, - f, - config, - offchainVersion, - offchainBytes, - ), - registry, - 'IncorrectNumberOfSigners', - ) - }) - - it('reverts if signers <= 3f', async () => { - newKeepers.pop() - await evmRevertCustomError( - registry - .connect(owner) - .setConfigTypeSafe( - newKeepers, - newKeepers, - f, - config, - offchainVersion, - offchainBytes, - ), - registry, - 'IncorrectNumberOfSigners', - ) - }) - - it('reverts on repeated signers', async () => { - const newSigners = [ - await personas.Eddy.getAddress(), - await personas.Eddy.getAddress(), - await personas.Eddy.getAddress(), - await personas.Eddy.getAddress(), - ] - await evmRevertCustomError( - registry - .connect(owner) - .setConfigTypeSafe( - newSigners, - newKeepers, - f, - config, - offchainVersion, - offchainBytes, - ), - registry, - 'RepeatedSigner', - ) - }) - - it('reverts on repeated transmitters', async () => { - const newTransmitters = [ - await personas.Eddy.getAddress(), - await personas.Eddy.getAddress(), - await personas.Eddy.getAddress(), - await personas.Eddy.getAddress(), - ] - await evmRevertCustomError( - registry - .connect(owner) - .setConfigTypeSafe( - newKeepers, - newTransmitters, - f, - config, - offchainVersion, - offchainBytes, - ), - registry, - 'RepeatedTransmitter', - ) - }) - - itMaybe('stores new config and emits event', async () => { - // Perform an upkeep so that totalPremium is updated - await registry.connect(admin).addFunds(upkeepId, toWei('100')) - let tx = await getTransmitTx(registry, keeper1, [upkeepId]) - await tx.wait() - - const newOffChainVersion = BigNumber.from('2') - const newOffChainConfig = '0x1122' - - const old = await registry.getState() - const oldState = old.state - assert(oldState.totalPremium.gt(BigNumber.from('0'))) - - const newSigners = newKeepers - tx = await registry - .connect(owner) - .setConfigTypeSafe( - newSigners, - newKeepers, - f, - config, - newOffChainVersion, - newOffChainConfig, - ) - - const updated = await registry.getState() - const updatedState = updated.state - assert(oldState.totalPremium.eq(updatedState.totalPremium)) - - // Old signer addresses which are not in new signers should be non active - for (let i = 0; i < signerAddresses.length; i++) { - const signer = signerAddresses[i] - if (!newSigners.includes(signer)) { - assert(!(await registry.getSignerInfo(signer)).active) - assert((await registry.getSignerInfo(signer)).index == 0) - } - } - // New signer addresses should be active - for (let i = 0; i < newSigners.length; i++) { - const signer = newSigners[i] - assert((await registry.getSignerInfo(signer)).active) - assert((await registry.getSignerInfo(signer)).index == i) - } - // Old transmitter addresses which are not in new transmitter should be non active, update lastCollected but retain other info - for (let i = 0; i < keeperAddresses.length; i++) { - const transmitter = keeperAddresses[i] - if (!newKeepers.includes(transmitter)) { - assert(!(await registry.getTransmitterInfo(transmitter)).active) - assert((await registry.getTransmitterInfo(transmitter)).index == i) - assert( - (await registry.getTransmitterInfo(transmitter)).lastCollected.eq( - oldState.totalPremium.sub( - oldState.totalPremium.mod(keeperAddresses.length), - ), - ), - ) - } - } - // New transmitter addresses should be active - for (let i = 0; i < newKeepers.length; i++) { - const transmitter = newKeepers[i] - assert((await registry.getTransmitterInfo(transmitter)).active) - assert((await registry.getTransmitterInfo(transmitter)).index == i) - assert( - (await registry.getTransmitterInfo(transmitter)).lastCollected.eq( - oldState.totalPremium, - ), - ) - } - - // config digest should be updated - assert(oldState.configCount + 1 == updatedState.configCount) - assert( - oldState.latestConfigBlockNumber != - updatedState.latestConfigBlockNumber, - ) - assert(oldState.latestConfigDigest != updatedState.latestConfigDigest) - - //New config should be updated - assert.deepEqual(updated.signers, newKeepers) - assert.deepEqual(updated.transmitters, newKeepers) - - // Event should have been emitted - await expect(tx).to.emit(registry, 'ConfigSet') - }) - }) - - describe('#setPeerRegistryMigrationPermission() / #getPeerRegistryMigrationPermission()', () => { - const peer = randomAddress() - it('allows the owner to set the peer registries', async () => { - let permission = await registry.getPeerRegistryMigrationPermission(peer) - expect(permission).to.equal(0) - await registry.setPeerRegistryMigrationPermission(peer, 1) - permission = await registry.getPeerRegistryMigrationPermission(peer) - expect(permission).to.equal(1) - await registry.setPeerRegistryMigrationPermission(peer, 2) - permission = await registry.getPeerRegistryMigrationPermission(peer) - expect(permission).to.equal(2) - await registry.setPeerRegistryMigrationPermission(peer, 0) - permission = await registry.getPeerRegistryMigrationPermission(peer) - expect(permission).to.equal(0) - }) - it('reverts if passed an unsupported permission', async () => { - await expect( - registry.connect(admin).setPeerRegistryMigrationPermission(peer, 10), - ).to.be.reverted - }) - it('reverts if not called by the owner', async () => { - await expect( - registry.connect(admin).setPeerRegistryMigrationPermission(peer, 1), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('#registerUpkeep', () => { - it('reverts when registry is paused', async () => { - await registry.connect(owner).pause() - await evmRevertCustomError( - registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), emptyBytes, '0x'), - registry, - 'RegistryPaused', - ) - }) - - it('reverts if the target is not a contract', async () => { - await evmRevertCustomError( - registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](zeroAddress, performGas, await admin.getAddress(), emptyBytes, '0x'), - registry, - 'NotAContract', - ) - }) - - it('reverts if called by a non-owner', async () => { - await evmRevertCustomError( - registry - .connect(keeper1) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), emptyBytes, '0x'), - registry, - 'OnlyCallableByOwnerOrRegistrar', - ) - }) - - it('reverts if execute gas is too low', async () => { - await evmRevertCustomError( - registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, 2299, await admin.getAddress(), emptyBytes, '0x'), - registry, - 'GasLimitOutsideRange', - ) - }) - - it('reverts if execute gas is too high', async () => { - await evmRevertCustomError( - registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, 5000001, await admin.getAddress(), emptyBytes, '0x'), - registry, - 'GasLimitOutsideRange', - ) - }) - - it('reverts if checkData is too long', async () => { - let longBytes = '0x' - for (let i = 0; i < 10000; i++) { - longBytes += '1' - } - await evmRevertCustomError( - registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), longBytes, '0x'), - registry, - 'CheckDataExceedsLimit', - ) - }) - - it('creates a record of the registration', async () => { - const performGases = [100000, 500000] - const checkDatas = [emptyBytes, '0x12'] - - for (let jdx = 0; jdx < performGases.length; jdx++) { - const performGas = performGases[jdx] - for (let kdx = 0; kdx < checkDatas.length; kdx++) { - const checkData = checkDatas[kdx] - const tx = await registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), checkData, '0x') - - //confirm the upkeep details and verify emitted events - const testUpkeepId = await getUpkeepID(tx) - await expect(tx) - .to.emit(registry, 'UpkeepRegistered') - .withArgs(testUpkeepId, performGas, await admin.getAddress()) - - await expect(tx) - .to.emit(registry, 'UpkeepCheckDataSet') - .withArgs(testUpkeepId, checkData) - await expect(tx) - .to.emit(registry, 'UpkeepTriggerConfigSet') - .withArgs(testUpkeepId, '0x') - - const registration = await registry.getUpkeep(testUpkeepId) - - assert.equal(mock.address, registration.target) - assert.notEqual( - ethers.constants.AddressZero, - await registry.getForwarder(testUpkeepId), - ) - assert.equal( - performGas.toString(), - registration.performGas.toString(), - ) - assert.equal(await admin.getAddress(), registration.admin) - assert.equal(0, registration.balance.toNumber()) - assert.equal(0, registration.amountSpent.toNumber()) - assert.equal(0, registration.lastPerformedBlockNumber) - assert.equal(checkData, registration.checkData) - assert.equal(registration.paused, false) - assert.equal(registration.offchainConfig, '0x') - assert(registration.maxValidBlocknumber.eq('0xffffffff')) - } - } - }) - }) - - describe('#pauseUpkeep', () => { - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry.connect(keeper1).pauseUpkeep(upkeepId.add(1)), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is already canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).pauseUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if the upkeep is already paused', async () => { - await registry.connect(admin).pauseUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).pauseUpkeep(upkeepId), - registry, - 'OnlyUnpausedUpkeep', - ) - }) - - it('reverts if the caller is not the upkeep admin', async () => { - await evmRevertCustomError( - registry.connect(keeper1).pauseUpkeep(upkeepId), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('pauses the upkeep and emits an event', async () => { - const tx = await registry.connect(admin).pauseUpkeep(upkeepId) - await expect(tx).to.emit(registry, 'UpkeepPaused').withArgs(upkeepId) - - const registration = await registry.getUpkeep(upkeepId) - assert.equal(registration.paused, true) - }) - }) - - describe('#unpauseUpkeep', () => { - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry.connect(keeper1).unpauseUpkeep(upkeepId.add(1)), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is already canceled', async () => { - await registry.connect(owner).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).unpauseUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('marks the contract as paused', async () => { - assert.isFalse((await registry.getState()).state.paused) - - await registry.connect(owner).pause() - - assert.isTrue((await registry.getState()).state.paused) - }) - - it('reverts if the upkeep is not paused', async () => { - await evmRevertCustomError( - registry.connect(admin).unpauseUpkeep(upkeepId), - registry, - 'OnlyPausedUpkeep', - ) - }) - - it('reverts if the caller is not the upkeep admin', async () => { - await registry.connect(admin).pauseUpkeep(upkeepId) - - const registration = await registry.getUpkeep(upkeepId) - - assert.equal(registration.paused, true) - - await evmRevertCustomError( - registry.connect(keeper1).unpauseUpkeep(upkeepId), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('unpauses the upkeep and emits an event', async () => { - const originalCount = (await registry.getActiveUpkeepIDs(0, 0)).length - - await registry.connect(admin).pauseUpkeep(upkeepId) - - const tx = await registry.connect(admin).unpauseUpkeep(upkeepId) - - await expect(tx).to.emit(registry, 'UpkeepUnpaused').withArgs(upkeepId) - - const registration = await registry.getUpkeep(upkeepId) - assert.equal(registration.paused, false) - - const upkeepIds = await registry.getActiveUpkeepIDs(0, 0) - assert.equal(upkeepIds.length, originalCount) - }) - }) - - describe('#setUpkeepCheckData', () => { - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry - .connect(keeper1) - .setUpkeepCheckData(upkeepId.add(1), randomBytes), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the caller is not upkeep admin', async () => { - await evmRevertCustomError( - registry.connect(keeper1).setUpkeepCheckData(upkeepId, randomBytes), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is cancelled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).setUpkeepCheckData(upkeepId, randomBytes), - registry, - 'UpkeepCancelled', - ) - }) - - it('is allowed to update on paused upkeep', async () => { - await registry.connect(admin).pauseUpkeep(upkeepId) - await registry.connect(admin).setUpkeepCheckData(upkeepId, randomBytes) - - const registration = await registry.getUpkeep(upkeepId) - assert.equal(randomBytes, registration.checkData) - }) - - it('reverts if new data exceeds limit', async () => { - let longBytes = '0x' - for (let i = 0; i < 10000; i++) { - longBytes += '1' - } - - await evmRevertCustomError( - registry.connect(admin).setUpkeepCheckData(upkeepId, longBytes), - registry, - 'CheckDataExceedsLimit', - ) - }) - - it('updates the upkeep check data and emits an event', async () => { - const tx = await registry - .connect(admin) - .setUpkeepCheckData(upkeepId, randomBytes) - await expect(tx) - .to.emit(registry, 'UpkeepCheckDataSet') - .withArgs(upkeepId, randomBytes) - - const registration = await registry.getUpkeep(upkeepId) - assert.equal(randomBytes, registration.checkData) - }) - }) - - describe('#setUpkeepGasLimit', () => { - const newGasLimit = BigNumber.from('300000') - - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry.connect(admin).setUpkeepGasLimit(upkeepId.add(1), newGasLimit), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(admin).setUpkeepGasLimit(upkeepId, newGasLimit), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevertCustomError( - registry.connect(owner).setUpkeepGasLimit(upkeepId, newGasLimit), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if new gas limit is out of bounds', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .setUpkeepGasLimit(upkeepId, BigNumber.from('100')), - registry, - 'GasLimitOutsideRange', - ) - await evmRevertCustomError( - registry - .connect(admin) - .setUpkeepGasLimit(upkeepId, BigNumber.from('6000000')), - registry, - 'GasLimitOutsideRange', - ) - }) - - it('updates the gas limit successfully', async () => { - const initialGasLimit = (await registry.getUpkeep(upkeepId)).performGas - assert.equal(initialGasLimit, performGas.toNumber()) - await registry.connect(admin).setUpkeepGasLimit(upkeepId, newGasLimit) - const updatedGasLimit = (await registry.getUpkeep(upkeepId)).performGas - assert.equal(updatedGasLimit, newGasLimit.toNumber()) - }) - - it('emits a log', async () => { - const tx = await registry - .connect(admin) - .setUpkeepGasLimit(upkeepId, newGasLimit) - await expect(tx) - .to.emit(registry, 'UpkeepGasLimitSet') - .withArgs(upkeepId, newGasLimit) - }) - }) - - describe('#setUpkeepOffchainConfig', () => { - const newConfig = '0xc0ffeec0ffee' - - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .setUpkeepOffchainConfig(upkeepId.add(1), newConfig), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(admin).setUpkeepOffchainConfig(upkeepId, newConfig), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevertCustomError( - registry.connect(owner).setUpkeepOffchainConfig(upkeepId, newConfig), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('updates the config successfully', async () => { - const initialConfig = (await registry.getUpkeep(upkeepId)).offchainConfig - assert.equal(initialConfig, '0x') - await registry.connect(admin).setUpkeepOffchainConfig(upkeepId, newConfig) - const updatedConfig = (await registry.getUpkeep(upkeepId)).offchainConfig - assert.equal(newConfig, updatedConfig) - }) - - it('emits a log', async () => { - const tx = await registry - .connect(admin) - .setUpkeepOffchainConfig(upkeepId, newConfig) - await expect(tx) - .to.emit(registry, 'UpkeepOffchainConfigSet') - .withArgs(upkeepId, newConfig) - }) - }) - - describe('#setUpkeepTriggerConfig', () => { - const newConfig = '0xdeadbeef' - - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .setUpkeepTriggerConfig(upkeepId.add(1), newConfig), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(admin).setUpkeepTriggerConfig(upkeepId, newConfig), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevertCustomError( - registry.connect(owner).setUpkeepTriggerConfig(upkeepId, newConfig), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('emits a log', async () => { - const tx = await registry - .connect(admin) - .setUpkeepTriggerConfig(upkeepId, newConfig) - await expect(tx) - .to.emit(registry, 'UpkeepTriggerConfigSet') - .withArgs(upkeepId, newConfig) - }) - }) - - describe('#transferUpkeepAdmin', () => { - it('reverts when called by anyone but the current upkeep admin', async () => { - await evmRevertCustomError( - registry - .connect(payee1) - .transferUpkeepAdmin(upkeepId, await payee2.getAddress()), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts when transferring to self', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await admin.getAddress()), - registry, - 'ValueNotChanged', - ) - }) - - it('reverts when the upkeep is cancelled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await keeper1.getAddress()), - registry, - 'UpkeepCancelled', - ) - }) - - it('allows cancelling transfer by reverting to zero address', async () => { - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - const tx = await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, ethers.constants.AddressZero) - - await expect(tx) - .to.emit(registry, 'UpkeepAdminTransferRequested') - .withArgs( - upkeepId, - await admin.getAddress(), - ethers.constants.AddressZero, - ) - }) - - it('does not change the upkeep admin', async () => { - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - - const upkeep = await registry.getUpkeep(upkeepId) - assert.equal(await admin.getAddress(), upkeep.admin) - }) - - it('emits an event announcing the new upkeep admin', async () => { - const tx = await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - - await expect(tx) - .to.emit(registry, 'UpkeepAdminTransferRequested') - .withArgs(upkeepId, await admin.getAddress(), await payee1.getAddress()) - }) - - it('does not emit an event when called with the same proposed upkeep admin', async () => { - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - - const tx = await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - const receipt = await tx.wait() - assert.equal(0, receipt.logs.length) - }) - }) - - describe('#acceptUpkeepAdmin', () => { - beforeEach(async () => { - // Start admin transfer to payee1 - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - }) - - it('reverts when not called by the proposed upkeep admin', async () => { - await evmRevertCustomError( - registry.connect(payee2).acceptUpkeepAdmin(upkeepId), - registry, - 'OnlyCallableByProposedAdmin', - ) - }) - - it('reverts when the upkeep is cancelled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(payee1).acceptUpkeepAdmin(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('does change the admin', async () => { - await registry.connect(payee1).acceptUpkeepAdmin(upkeepId) - - const upkeep = await registry.getUpkeep(upkeepId) - assert.equal(await payee1.getAddress(), upkeep.admin) - }) - - it('emits an event announcing the new upkeep admin', async () => { - const tx = await registry.connect(payee1).acceptUpkeepAdmin(upkeepId) - await expect(tx) - .to.emit(registry, 'UpkeepAdminTransferred') - .withArgs(upkeepId, await admin.getAddress(), await payee1.getAddress()) - }) - }) - - describe('#withdrawOwnerFunds', () => { - it('can only be called by owner', async () => { - await evmRevert( - registry.connect(keeper1).withdrawOwnerFunds(), - 'Only callable by owner', - ) - }) - - itMaybe('withdraws the collected fees to owner', async () => { - await registry.connect(admin).addFunds(upkeepId, toWei('100')) - // Very high min spend, whole balance as cancellation fees - const minUpkeepSpend = toWei('1000') - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - }, - offchainVersion, - offchainBytes, - ) - const upkeepBalance = (await registry.getUpkeep(upkeepId)).balance - const ownerBefore = await linkToken.balanceOf(await owner.getAddress()) - - await registry.connect(owner).cancelUpkeep(upkeepId) - - // Transfered to owner balance on registry - let ownerRegistryBalance = (await registry.getState()).state - .ownerLinkBalance - assert.isTrue(ownerRegistryBalance.eq(upkeepBalance)) - - // Now withdraw - await registry.connect(owner).withdrawOwnerFunds() - - ownerRegistryBalance = (await registry.getState()).state.ownerLinkBalance - const ownerAfter = await linkToken.balanceOf(await owner.getAddress()) - - // Owner registry balance should be changed to 0 - assert.isTrue(ownerRegistryBalance.eq(BigNumber.from('0'))) - - // Owner should be credited with the balance - assert.isTrue(ownerBefore.add(upkeepBalance).eq(ownerAfter)) - }) - }) - - describe('#transferPayeeship', () => { - it('reverts when called by anyone but the current payee', async () => { - await evmRevertCustomError( - registry - .connect(payee2) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ), - registry, - 'OnlyCallableByPayee', - ) - }) - - it('reverts when transferring to self', async () => { - await evmRevertCustomError( - registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee1.getAddress(), - ), - registry, - 'ValueNotChanged', - ) - }) - - it('does not change the payee', async () => { - await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - - const info = await registry.getTransmitterInfo(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 evmRevertCustomError( - registry.connect(payee1).acceptPayeeship(await keeper1.getAddress()), - registry, - 'OnlyCallableByProposedPayee', - ) - }) - - 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.getTransmitterInfo(await keeper1.getAddress()) - assert.equal(await payee2.getAddress(), info.payee) - }) - }) - - 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.getState()).state.paused) - - await registry.connect(owner).pause() - - assert.isTrue((await registry.getState()).state.paused) - }) - - it('Does not allow transmits when paused', async () => { - await registry.connect(owner).pause() - - await evmRevertCustomError( - getTransmitTx(registry, keeper1, [upkeepId]), - registry, - 'RegistryPaused', - ) - }) - - it('Does not allow creation of new upkeeps when paused', async () => { - await registry.connect(owner).pause() - - await evmRevertCustomError( - registry - .connect(owner) - [ - 'registerUpkeep(address,uint32,address,bytes,bytes)' - ](mock.address, performGas, await admin.getAddress(), emptyBytes, '0x'), - registry, - 'RegistryPaused', - ) - }) - }) - - 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.getState()).state.paused) - - await registry.connect(owner).unpause() - - assert.isFalse((await registry.getState()).state.paused) - }) - }) - - describe('#migrateUpkeeps() / #receiveUpkeeps()', async () => { - context('when permissions are set', () => { - beforeEach(async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(upkeepId, toWei('100')) - await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 1) - await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 2) - }) - - it('migrates an upkeep', async () => { - const offchainBytes = '0x987654abcd' - await registry - .connect(admin) - .setUpkeepOffchainConfig(upkeepId, offchainBytes) - const reg1Upkeep = await registry.getUpkeep(upkeepId) - const forwarderAddress = await registry.getForwarder(upkeepId) - expect(reg1Upkeep.balance).to.equal(toWei('100')) - expect(reg1Upkeep.checkData).to.equal(randomBytes) - expect(forwarderAddress).to.not.equal(ethers.constants.AddressZero) - expect(reg1Upkeep.offchainConfig).to.equal(offchainBytes) - expect((await registry.getState()).state.numUpkeeps).to.equal( - numUpkeeps, - ) - const forwarder = IAutomationForwarderFactory.connect( - forwarderAddress, - owner, - ) - expect(await forwarder.getRegistry()).to.equal(registry.address) - // Set an upkeep admin transfer in progress too - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - - // migrate - await registry - .connect(admin) - .migrateUpkeeps([upkeepId], mgRegistry.address) - expect((await registry.getState()).state.numUpkeeps).to.equal( - numUpkeeps - 1, - ) - expect((await mgRegistry.getState()).state.numUpkeeps).to.equal(1) - expect((await registry.getUpkeep(upkeepId)).balance).to.equal(0) - expect((await registry.getUpkeep(upkeepId)).checkData).to.equal('0x') - expect((await mgRegistry.getUpkeep(upkeepId)).balance).to.equal( - toWei('100'), - ) - expect( - (await mgRegistry.getState()).state.expectedLinkBalance, - ).to.equal(toWei('100')) - expect((await mgRegistry.getUpkeep(upkeepId)).checkData).to.equal( - randomBytes, - ) - expect((await mgRegistry.getUpkeep(upkeepId)).offchainConfig).to.equal( - offchainBytes, - ) - expect(await mgRegistry.getForwarder(upkeepId)).to.equal( - forwarderAddress, - ) - // test that registry is updated on forwarder - expect(await forwarder.getRegistry()).to.equal(mgRegistry.address) - // migration will delete the upkeep and nullify admin transfer - await expect( - registry.connect(payee1).acceptUpkeepAdmin(upkeepId), - ).to.be.revertedWithCustomError(registry, 'UpkeepCancelled') - await expect( - mgRegistry.connect(payee1).acceptUpkeepAdmin(upkeepId), - ).to.be.revertedWithCustomError( - mgRegistry, - 'OnlyCallableByProposedAdmin', - ) - }) - - it('migrates a paused upkeep', async () => { - expect((await registry.getUpkeep(upkeepId)).balance).to.equal( - toWei('100'), - ) - expect((await registry.getUpkeep(upkeepId)).checkData).to.equal( - randomBytes, - ) - expect((await registry.getState()).state.numUpkeeps).to.equal( - numUpkeeps, - ) - await registry.connect(admin).pauseUpkeep(upkeepId) - // verify the upkeep is paused - expect((await registry.getUpkeep(upkeepId)).paused).to.equal(true) - // migrate - await registry - .connect(admin) - .migrateUpkeeps([upkeepId], mgRegistry.address) - expect((await registry.getState()).state.numUpkeeps).to.equal( - numUpkeeps - 1, - ) - expect((await mgRegistry.getState()).state.numUpkeeps).to.equal(1) - expect((await registry.getUpkeep(upkeepId)).balance).to.equal(0) - expect((await mgRegistry.getUpkeep(upkeepId)).balance).to.equal( - toWei('100'), - ) - expect((await registry.getUpkeep(upkeepId)).checkData).to.equal('0x') - expect((await mgRegistry.getUpkeep(upkeepId)).checkData).to.equal( - randomBytes, - ) - expect( - (await mgRegistry.getState()).state.expectedLinkBalance, - ).to.equal(toWei('100')) - // verify the upkeep is still paused after migration - expect((await mgRegistry.getUpkeep(upkeepId)).paused).to.equal(true) - }) - - it('emits an event on both contracts', async () => { - expect((await registry.getUpkeep(upkeepId)).balance).to.equal( - toWei('100'), - ) - expect((await registry.getUpkeep(upkeepId)).checkData).to.equal( - randomBytes, - ) - expect((await registry.getState()).state.numUpkeeps).to.equal( - numUpkeeps, - ) - const tx = registry - .connect(admin) - .migrateUpkeeps([upkeepId], mgRegistry.address) - await expect(tx) - .to.emit(registry, 'UpkeepMigrated') - .withArgs(upkeepId, toWei('100'), mgRegistry.address) - await expect(tx) - .to.emit(mgRegistry, 'UpkeepReceived') - .withArgs(upkeepId, toWei('100'), registry.address) - }) - - it('is only migratable by the admin', async () => { - await expect( - registry - .connect(owner) - .migrateUpkeeps([upkeepId], mgRegistry.address), - ).to.be.revertedWithCustomError(registry, 'OnlyCallableByAdmin') - await registry - .connect(admin) - .migrateUpkeeps([upkeepId], mgRegistry.address) - }) - }) - - context('when permissions are not set', () => { - it('reverts', async () => { - // no permissions - await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 0) - await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 0) - await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to - .be.reverted - // only outgoing permissions - await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 1) - await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 0) - await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to - .be.reverted - // only incoming permissions - await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 0) - await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 2) - await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to - .be.reverted - // permissions opposite direction - await registry.setPeerRegistryMigrationPermission(mgRegistry.address, 2) - await mgRegistry.setPeerRegistryMigrationPermission(registry.address, 1) - await expect(registry.migrateUpkeeps([upkeepId], mgRegistry.address)).to - .be.reverted - }) - }) - }) - - describe('#setPayees', () => { - const IGNORE_ADDRESS = '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF' - - it('reverts when not called by the owner', async () => { - await evmRevert( - registry.connect(keeper1).setPayees(payees), - 'Only callable by owner', - ) - }) - - it('reverts with different numbers of payees than transmitters', async () => { - await evmRevertCustomError( - registry.connect(owner).setPayees([...payees, randomAddress()]), - registry, - 'ParameterLengthError', - ) - }) - - it('reverts if the payee is the zero address', async () => { - await blankRegistry.connect(owner).setConfigTypeSafe(...baseConfig) // used to test initial config - - await evmRevertCustomError( - blankRegistry // used to test initial config - .connect(owner) - .setPayees([ethers.constants.AddressZero, ...payees.slice(1)]), - registry, - 'InvalidPayee', - ) - }) - - itMaybe( - 'sets the payees when exisitng payees are zero address', - async () => { - //Initial payees should be zero address - await blankRegistry.connect(owner).setConfigTypeSafe(...baseConfig) // used to test initial config - - for (let i = 0; i < keeperAddresses.length; i++) { - const payee = ( - await blankRegistry.getTransmitterInfo(keeperAddresses[i]) - ).payee // used to test initial config - assert.equal(payee, zeroAddress) - } - - await blankRegistry.connect(owner).setPayees(payees) // used to test initial config - - for (let i = 0; i < keeperAddresses.length; i++) { - const payee = ( - await blankRegistry.getTransmitterInfo(keeperAddresses[i]) - ).payee - assert.equal(payee, payees[i]) - } - }, - ) - - it('does not change the payee if IGNORE_ADDRESS is used as payee', async () => { - const signers = Array.from({ length: 5 }, randomAddress) - const keepers = Array.from({ length: 5 }, randomAddress) - const payees = Array.from({ length: 5 }, randomAddress) - const newTransmitter = randomAddress() - const newPayee = randomAddress() - const ignoreAddresses = new Array(payees.length).fill(IGNORE_ADDRESS) - const newPayees = [...ignoreAddresses, newPayee] - // arbitrum registry - // configure registry with 5 keepers // optimism registry - await blankRegistry // used to test initial configurations - .connect(owner) - .setConfigTypeSafe( - signers, - keepers, - f, - config, - offchainVersion, - offchainBytes, - ) - // arbitrum registry - // set initial payees // optimism registry - await blankRegistry.connect(owner).setPayees(payees) // used to test initial configurations - // arbitrum registry - // add another keeper // optimism registry - await blankRegistry // used to test initial configurations - .connect(owner) - .setConfigTypeSafe( - [...signers, randomAddress()], - [...keepers, newTransmitter], - f, - config, - offchainVersion, - offchainBytes, - ) - // arbitrum registry - // update payee list // optimism registry // arbitrum registry - await blankRegistry.connect(owner).setPayees(newPayees) // used to test initial configurations // optimism registry - const ignored = await blankRegistry.getTransmitterInfo(newTransmitter) // used to test initial configurations - assert.equal(newPayee, ignored.payee) - assert.equal(true, ignored.active) - }) - - it('reverts if payee is non zero and owner tries to change payee', async () => { - const newPayees = [randomAddress(), ...payees.slice(1)] - - await evmRevertCustomError( - registry.connect(owner).setPayees(newPayees), - registry, - 'InvalidPayee', - ) - }) - - it('emits events for every payee added and removed', async () => { - const tx = await registry.connect(owner).setPayees(payees) - await expect(tx) - .to.emit(registry, 'PayeesUpdated') - .withArgs(keeperAddresses, payees) - }) - }) - - describe('#cancelUpkeep', () => { - it('reverts if the ID is not valid', async () => { - await evmRevertCustomError( - registry.connect(owner).cancelUpkeep(upkeepId.add(1)), - registry, - 'CannotCancel', - ) - }) - - it('reverts if called by a non-owner/non-admin', async () => { - await evmRevertCustomError( - registry.connect(keeper1).cancelUpkeep(upkeepId), - registry, - 'OnlyCallableByOwnerOrAdmin', - ) - }) - - describe('when called by the owner', async () => { - it('sets the registration to invalid immediately', async () => { - const tx = await registry.connect(owner).cancelUpkeep(upkeepId) - const receipt = await tx.wait() - const registration = await registry.getUpkeep(upkeepId) - assert.equal( - registration.maxValidBlocknumber.toNumber(), - receipt.blockNumber, - ) - }) - - it('emits an event', async () => { - const tx = await registry.connect(owner).cancelUpkeep(upkeepId) - const receipt = await tx.wait() - await expect(tx) - .to.emit(registry, 'UpkeepCanceled') - .withArgs(upkeepId, BigNumber.from(receipt.blockNumber)) - }) - - it('immediately prevents upkeep', async () => { - await registry.connect(owner).cancelUpkeep(upkeepId) - - const tx = await getTransmitTx(registry, keeper1, [upkeepId]) - const receipt = await tx.wait() - const cancelledUpkeepReportLogs = - parseCancelledUpkeepReportLogs(receipt) - // exactly 1 CancelledUpkeepReport log should be emitted - assert.equal(cancelledUpkeepReportLogs.length, 1) - }) - - it('does not revert if reverts if called multiple times', async () => { - await registry.connect(owner).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(owner).cancelUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - describe('when called by the owner when the admin has just canceled', () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - let oldExpiration: BigNumber - - beforeEach(async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - const registration = await registry.getUpkeep(upkeepId) - oldExpiration = registration.maxValidBlocknumber - }) - - it('reverts with proper error', async () => { - await evmRevertCustomError( - registry.connect(owner).cancelUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - }) - }) - - describe('when called by the admin', async () => { - it('reverts if called again by the admin', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).cancelUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if called by the owner after the timeout', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - for (let i = 0; i < cancellationDelay; i++) { - await ethers.provider.send('evm_mine', []) - } - - await evmRevertCustomError( - registry.connect(owner).cancelUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('sets the registration to invalid in 50 blocks', async () => { - const tx = await registry.connect(admin).cancelUpkeep(upkeepId) - const receipt = await tx.wait() - const registration = await registry.getUpkeep(upkeepId) - assert.equal( - registration.maxValidBlocknumber.toNumber(), - receipt.blockNumber + 50, - ) - }) - - it('emits an event', async () => { - const tx = await registry.connect(admin).cancelUpkeep(upkeepId) - const receipt = await tx.wait() - await expect(tx) - .to.emit(registry, 'UpkeepCanceled') - .withArgs( - upkeepId, - BigNumber.from(receipt.blockNumber + cancellationDelay), - ) - }) - - it('immediately prevents upkeep', async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(upkeepId, toWei('100')) - await registry.connect(admin).cancelUpkeep(upkeepId) - - await getTransmitTx(registry, keeper1, [upkeepId]) - - for (let i = 0; i < cancellationDelay; i++) { - await ethers.provider.send('evm_mine', []) - } - - const tx = await getTransmitTx(registry, keeper1, [upkeepId]) - - const receipt = await tx.wait() - const cancelledUpkeepReportLogs = - parseCancelledUpkeepReportLogs(receipt) - // exactly 1 CancelledUpkeepReport log should be emitted - assert.equal(cancelledUpkeepReportLogs.length, 1) - }) - - describeMaybe('when an upkeep has been performed', async () => { - beforeEach(async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(upkeepId, toWei('100')) - await getTransmitTx(registry, keeper1, [upkeepId]) - }) - - it('deducts a cancellation fee from the upkeep and gives to owner', async () => { - const minUpkeepSpend = toWei('10') - - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - }, - offchainVersion, - offchainBytes, - ) - - const payee1Before = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance - const ownerBefore = (await registry.getState()).state.ownerLinkBalance - - const amountSpent = toWei('100').sub(upkeepBefore) - const cancellationFee = minUpkeepSpend.sub(amountSpent) - - await registry.connect(admin).cancelUpkeep(upkeepId) - - const payee1After = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance - const ownerAfter = (await registry.getState()).state.ownerLinkBalance - - // post upkeep balance should be previous balance minus cancellation fee - assert.isTrue(upkeepBefore.sub(cancellationFee).eq(upkeepAfter)) - // payee balance should not change - assert.isTrue(payee1Before.eq(payee1After)) - // owner should receive the cancellation fee - assert.isTrue(ownerAfter.sub(ownerBefore).eq(cancellationFee)) - }) - - it('deducts up to balance as cancellation fee', async () => { - // Very high min spend, should deduct whole balance as cancellation fees - const minUpkeepSpend = toWei('1000') - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - }, - offchainVersion, - offchainBytes, - ) - const payee1Before = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance - const ownerBefore = (await registry.getState()).state.ownerLinkBalance - - await registry.connect(admin).cancelUpkeep(upkeepId) - const payee1After = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const ownerAfter = (await registry.getState()).state.ownerLinkBalance - const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance - - // all upkeep balance is deducted for cancellation fee - assert.equal(0, upkeepAfter.toNumber()) - // payee balance should not change - assert.isTrue(payee1After.eq(payee1Before)) - // all upkeep balance is transferred to the owner - assert.isTrue(ownerAfter.sub(ownerBefore).eq(upkeepBefore)) - }) - - it('does not deduct cancellation fee if more than minUpkeepSpend is spent', async () => { - // Very low min spend, already spent in one perform upkeep - const minUpkeepSpend = BigNumber.from(420) - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - }, - offchainVersion, - offchainBytes, - ) - const payee1Before = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance - const ownerBefore = (await registry.getState()).state.ownerLinkBalance - - await registry.connect(admin).cancelUpkeep(upkeepId) - const payee1After = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const ownerAfter = (await registry.getState()).state.ownerLinkBalance - const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance - - // upkeep does not pay cancellation fee after cancellation because minimum upkeep spent is met - assert.isTrue(upkeepBefore.eq(upkeepAfter)) - // owner balance does not change - assert.isTrue(ownerAfter.eq(ownerBefore)) - // payee balance does not change - assert.isTrue(payee1Before.eq(payee1After)) - }) - }) - }) - }) - - describe('#withdrawPayment', () => { - beforeEach(async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(upkeepId, toWei('100')) - await getTransmitTx(registry, keeper1, [upkeepId]) - }) - - it('reverts if called by anyone but the payee', async () => { - await evmRevertCustomError( - registry - .connect(payee2) - .withdrawPayment( - await keeper1.getAddress(), - await nonkeeper.getAddress(), - ), - registry, - 'OnlyCallableByPayee', - ) - }) - - it('reverts if called with the 0 address', async () => { - await evmRevertCustomError( - registry - .connect(payee2) - .withdrawPayment(await keeper1.getAddress(), zeroAddress), - registry, - 'InvalidRecipient', - ) - }) - - it('updates the balances', async () => { - const to = await nonkeeper.getAddress() - const keeperBefore = await registry.getTransmitterInfo( - await keeper1.getAddress(), - ) - const registrationBefore = (await registry.getUpkeep(upkeepId)).balance - const toLinkBefore = await linkToken.balanceOf(to) - const registryLinkBefore = await linkToken.balanceOf(registry.address) - const registryPremiumBefore = (await registry.getState()).state - .totalPremium - const ownerBefore = (await registry.getState()).state.ownerLinkBalance - - // Withdrawing for first time, last collected = 0 - assert.equal(keeperBefore.lastCollected.toString(), '0') - - //// Do the thing - await registry - .connect(payee1) - .withdrawPayment(await keeper1.getAddress(), to) - - const keeperAfter = await registry.getTransmitterInfo( - await keeper1.getAddress(), - ) - const registrationAfter = (await registry.getUpkeep(upkeepId)).balance - const toLinkAfter = await linkToken.balanceOf(to) - const registryLinkAfter = await linkToken.balanceOf(registry.address) - const registryPremiumAfter = (await registry.getState()).state - .totalPremium - const ownerAfter = (await registry.getState()).state.ownerLinkBalance - - // registry total premium should not change - assert.isTrue(registryPremiumBefore.eq(registryPremiumAfter)) - - // Last collected should be updated to premium-change - assert.isTrue( - keeperAfter.lastCollected.eq( - registryPremiumBefore.sub( - registryPremiumBefore.mod(keeperAddresses.length), - ), - ), - ) - - // owner balance should remain unchanged - assert.isTrue(ownerAfter.eq(ownerBefore)) - - assert.isTrue(keeperAfter.balance.eq(BigNumber.from(0))) - assert.isTrue(registrationBefore.eq(registrationAfter)) - assert.isTrue(toLinkBefore.add(keeperBefore.balance).eq(toLinkAfter)) - assert.isTrue( - registryLinkBefore.sub(keeperBefore.balance).eq(registryLinkAfter), - ) - }) - - it('emits a log announcing the withdrawal', async () => { - const balance = ( - await registry.getTransmitterInfo(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('#checkCallback', () => { - it('returns false with appropriate failure reason when target callback reverts', async () => { - await streamsLookupUpkeep.setShouldRevertCallback(true) - - const values: any[] = ['0x1234', '0xabcd'] - const res = await registry - .connect(zeroAddress) - .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') - - assert.isFalse(res.upkeepNeeded) - assert.equal(res.performData, '0x') - assert.equal( - res.upkeepFailureReason, - UpkeepFailureReason.CHECK_CALLBACK_REVERTED, - ) - assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - }) - - it('returns false with appropriate failure reason when target callback returns big performData', async () => { - let longBytes = '0x' - for (let i = 0; i <= maxPerformDataSize.toNumber(); i++) { - longBytes += '11' - } - const values: any[] = [longBytes, longBytes] - const res = await registry - .connect(zeroAddress) - .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') - - assert.isFalse(res.upkeepNeeded) - assert.equal(res.performData, '0x') - assert.equal( - res.upkeepFailureReason, - UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, - ) - assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - }) - - it('returns false with appropriate failure reason when target callback returns false', async () => { - await streamsLookupUpkeep.setCallbackReturnBool(false) - const values: any[] = ['0x1234', '0xabcd'] - const res = await registry - .connect(zeroAddress) - .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') - - assert.isFalse(res.upkeepNeeded) - assert.equal(res.performData, '0x') - assert.equal( - res.upkeepFailureReason, - UpkeepFailureReason.UPKEEP_NOT_NEEDED, - ) - assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - }) - - it('succeeds with upkeep needed', async () => { - const values: any[] = ['0x1234', '0xabcd'] - - const res = await registry - .connect(zeroAddress) - .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') - const expectedPerformData = ethers.utils.defaultAbiCoder.encode( - ['bytes[]', 'bytes'], - [values, '0x'], - ) - - assert.isTrue(res.upkeepNeeded) - assert.equal(res.performData, expectedPerformData) - assert.equal(res.upkeepFailureReason, UpkeepFailureReason.NONE) - assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used - }) - }) - - describe('#setUpkeepPrivilegeConfig() / #getUpkeepPrivilegeConfig()', () => { - it('reverts when non manager tries to set privilege config', async () => { - await evmRevertCustomError( - registry.connect(payee3).setUpkeepPrivilegeConfig(upkeepId, '0x1234'), - registry, - 'OnlyCallableByUpkeepPrivilegeManager', - ) - }) - - it('returns empty bytes for upkeep privilege config before setting', async () => { - const cfg = await registry.getUpkeepPrivilegeConfig(upkeepId) - assert.equal(cfg, '0x') - }) - - it('allows upkeep manager to set privilege config', async () => { - const tx = await registry - .connect(personas.Norbert) - .setUpkeepPrivilegeConfig(upkeepId, '0x1234') - await expect(tx) - .to.emit(registry, 'UpkeepPrivilegeConfigSet') - .withArgs(upkeepId, '0x1234') - - const cfg = await registry.getUpkeepPrivilegeConfig(upkeepId) - assert.equal(cfg, '0x1234') - }) - }) - - describe('#setAdminPrivilegeConfig() / #getAdminPrivilegeConfig()', () => { - const admin = randomAddress() - - it('reverts when non manager tries to set privilege config', async () => { - await evmRevertCustomError( - registry.connect(payee3).setAdminPrivilegeConfig(admin, '0x1234'), - registry, - 'OnlyCallableByUpkeepPrivilegeManager', - ) - }) - - it('returns empty bytes for upkeep privilege config before setting', async () => { - const cfg = await registry.getAdminPrivilegeConfig(admin) - assert.equal(cfg, '0x') - }) - - it('allows upkeep manager to set privilege config', async () => { - const tx = await registry - .connect(personas.Norbert) - .setAdminPrivilegeConfig(admin, '0x1234') - await expect(tx) - .to.emit(registry, 'AdminPrivilegeConfigSet') - .withArgs(admin, '0x1234') - - const cfg = await registry.getAdminPrivilegeConfig(admin) - assert.equal(cfg, '0x1234') - }) - }) - - describe('transmitterPremiumSplit [ @skip-coverage ]', () => { - beforeEach(async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(upkeepId, toWei('100')) - }) - - it('splits premium evenly across transmitters', async () => { - // Do a transmit from keeper1 - await getTransmitTx(registry, keeper1, [upkeepId]) - - const registryPremium = (await registry.getState()).state.totalPremium - assert.isTrue(registryPremium.gt(BigNumber.from(0))) - - const premiumPerTransmitter = registryPremium.div( - BigNumber.from(keeperAddresses.length), - ) - const k1Balance = ( - await registry.getTransmitterInfo(await keeper1.getAddress()) - ).balance - // transmitter should be reimbursed for gas and get the premium - assert.isTrue(k1Balance.gt(premiumPerTransmitter)) - const k1GasReimbursement = k1Balance.sub(premiumPerTransmitter) - - const k2Balance = ( - await registry.getTransmitterInfo(await keeper2.getAddress()) - ).balance - // non transmitter should get its share of premium - assert.isTrue(k2Balance.eq(premiumPerTransmitter)) - - // Now do a transmit from keeper 2 - await getTransmitTx(registry, keeper2, [upkeepId]) - const registryPremiumNew = (await registry.getState()).state.totalPremium - assert.isTrue(registryPremiumNew.gt(registryPremium)) - const premiumPerTransmitterNew = registryPremiumNew.div( - BigNumber.from(keeperAddresses.length), - ) - const additionalPremium = premiumPerTransmitterNew.sub( - premiumPerTransmitter, - ) - - const k1BalanceNew = ( - await registry.getTransmitterInfo(await keeper1.getAddress()) - ).balance - // k1 should get the new premium - assert.isTrue( - k1BalanceNew.eq(k1GasReimbursement.add(premiumPerTransmitterNew)), - ) - - const k2BalanceNew = ( - await registry.getTransmitterInfo(await keeper2.getAddress()) - ).balance - // k2 should get gas reimbursement in addition to new premium - assert.isTrue(k2BalanceNew.gt(k2Balance.add(additionalPremium))) - }) - - it('updates last collected upon payment withdrawn', async () => { - // Do a transmit from keeper1 - await getTransmitTx(registry, keeper1, [upkeepId]) - - const registryPremium = (await registry.getState()).state.totalPremium - const k1 = await registry.getTransmitterInfo(await keeper1.getAddress()) - const k2 = await registry.getTransmitterInfo(await keeper2.getAddress()) - - // Withdrawing for first time, last collected = 0 - assert.isTrue(k1.lastCollected.eq(BigNumber.from(0))) - assert.isTrue(k2.lastCollected.eq(BigNumber.from(0))) - - //// Do the thing - await registry - .connect(payee1) - .withdrawPayment( - await keeper1.getAddress(), - await nonkeeper.getAddress(), - ) - - const k1New = await registry.getTransmitterInfo( - await keeper1.getAddress(), - ) - const k2New = await registry.getTransmitterInfo( - await keeper2.getAddress(), - ) - - // transmitter info lastCollected should be updated for k1, not for k2 - assert.isTrue( - k1New.lastCollected.eq( - registryPremium.sub(registryPremium.mod(keeperAddresses.length)), - ), - ) - assert.isTrue(k2New.lastCollected.eq(BigNumber.from(0))) - }) - - itMaybe( - 'maintains consistent balance information across all parties', - async () => { - // throughout transmits, withdrawals, setConfigs total claim on balances should remain less than expected balance - // some spare change can get lost but it should be less than maxAllowedSpareChange - - let maxAllowedSpareChange = BigNumber.from('0') - await verifyConsistentAccounting(maxAllowedSpareChange) - - await getTransmitTx(registry, keeper1, [upkeepId]) - maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31')) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await registry - .connect(payee1) - .withdrawPayment( - await keeper1.getAddress(), - await nonkeeper.getAddress(), - ) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await registry - .connect(payee2) - .withdrawPayment( - await keeper2.getAddress(), - await nonkeeper.getAddress(), - ) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await getTransmitTx(registry, keeper1, [upkeepId]) - maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31')) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await registry.connect(owner).setConfigTypeSafe( - signerAddresses.slice(2, 15), // only use 2-14th index keepers - keeperAddresses.slice(2, 15), - f, - config, - offchainVersion, - offchainBytes, - ) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await getTransmitTx(registry, keeper3, [upkeepId], { - startingSignerIndex: 2, - }) - maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('13')) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await registry - .connect(payee1) - .withdrawPayment( - await keeper1.getAddress(), - await nonkeeper.getAddress(), - ) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await registry - .connect(payee3) - .withdrawPayment( - await keeper3.getAddress(), - await nonkeeper.getAddress(), - ) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await registry.connect(owner).setConfigTypeSafe( - signerAddresses.slice(0, 4), // only use 0-3rd index keepers - keeperAddresses.slice(0, 4), - f, - config, - offchainVersion, - offchainBytes, - ) - await verifyConsistentAccounting(maxAllowedSpareChange) - await getTransmitTx(registry, keeper1, [upkeepId]) - maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4')) - await getTransmitTx(registry, keeper3, [upkeepId]) - maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4')) - - await verifyConsistentAccounting(maxAllowedSpareChange) - await registry - .connect(payee5) - .withdrawPayment( - await keeper5.getAddress(), - await nonkeeper.getAddress(), - ) - await verifyConsistentAccounting(maxAllowedSpareChange) - - await registry - .connect(payee1) - .withdrawPayment( - await keeper1.getAddress(), - await nonkeeper.getAddress(), - ) - await verifyConsistentAccounting(maxAllowedSpareChange) - }, - ) - }) -}) diff --git a/contracts/test/v0.8/automation/CronUpkeep.test.ts b/contracts/test/v0.8/automation/CronUpkeep.test.ts deleted file mode 100644 index 7b769797f12..00000000000 --- a/contracts/test/v0.8/automation/CronUpkeep.test.ts +++ /dev/null @@ -1,576 +0,0 @@ -import moment from 'moment' -import { ethers } from 'hardhat' -import { Contract } from 'ethers' -import { assert, expect } from 'chai' -import { CronUpkeepTestHelper } from '../../../typechain/CronUpkeepTestHelper' -import { CronUpkeepDelegate } from '../../../typechain/CronUpkeepDelegate' -import { CronUpkeepFactory } from '../../../typechain/CronUpkeepFactory' -import { CronUpkeepTestHelper__factory as CronUpkeepTestHelperFactory } from '../../../typechain/factories/CronUpkeepTestHelper__factory' -import { CronInternalTestHelper } from '../../../typechain/CronInternalTestHelper' -import { CronReceiver } from '../../../typechain/CronReceiver' -import { BigNumber, BigNumberish } from '@ethersproject/bignumber' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { validCrons } from '../../test-helpers/fixtures' -import * as h from '../../test-helpers/helpers' - -const { utils } = ethers -const { AddressZero } = ethers.constants - -const OWNABLE_ERR = 'Only callable by owner' -const CRON_NOT_FOUND_ERR = 'CronJobIDNotFound' - -let cron: CronUpkeepTestHelper -let cronFactory: CronUpkeepTestHelperFactory // the typechain factory that deploys cron upkeep contracts -let cronFactoryContract: CronUpkeepFactory // the cron factory contract -let cronDelegate: CronUpkeepDelegate -let cronTestHelper: CronInternalTestHelper -let cronReceiver1: CronReceiver -let cronReceiver2: CronReceiver - -let admin: SignerWithAddress -let owner: SignerWithAddress -let stranger: SignerWithAddress - -const timeStamp = 32503680000 // Jan 1, 3000 12:00AM -const basicCronString = '0 * * * *' - -let handler1Sig: string -let handler2Sig: string -let revertHandlerSig: string -let basicSpec: string - -async function assertJobIDsEqual(expected: number[]) { - const ids = (await cron.getActiveCronJobIDs()).map((n) => n.toNumber()) - assert.deepEqual(ids.sort(), expected.sort()) -} - -function decodePayload(payload: string) { - return utils.defaultAbiCoder.decode( - ['uint256', 'uint256', 'address', 'bytes'], - payload, - ) as [BigNumber, BigNumber, string, string] -} - -function encodePayload(payload: [BigNumberish, BigNumberish, string, string]) { - return utils.defaultAbiCoder.encode( - ['uint256', 'uint256', 'address', 'bytes'], - payload, - ) -} - -async function createBasicCron() { - return await cron.createCronJobFromEncodedSpec( - cronReceiver1.address, - handler1Sig, - basicSpec, - ) -} - -describe('CronUpkeep', () => { - beforeEach(async () => { - const accounts = await ethers.getSigners() - admin = accounts[0] - owner = accounts[1] - stranger = accounts[2] - const crFactory = await ethers.getContractFactory('CronReceiver', owner) - cronReceiver1 = await crFactory.deploy() - cronReceiver2 = await crFactory.deploy() - const cronDelegateFactory = await ethers.getContractFactory( - 'CronUpkeepDelegate', - admin, - ) - cronDelegate = await cronDelegateFactory.deploy() - const cronExternalFactory = await ethers.getContractFactory( - 'src/v0.8/automation/libraries/external/Cron.sol:Cron', - admin, - ) - const cronExternalLib = await cronExternalFactory.deploy() - cronFactory = await ethers.getContractFactory('CronUpkeepTestHelper', { - signer: admin, - libraries: { Cron: cronExternalLib.address }, - }) - cron = ( - await cronFactory.deploy(owner.address, cronDelegate.address, 5, []) - ).connect(owner) - const cronFactoryContractFactory = await ethers.getContractFactory( - 'CronUpkeepFactory', - { signer: admin, libraries: { Cron: cronExternalLib.address } }, - ) // the typechain factory that creates the cron factory contract - cronFactoryContract = await cronFactoryContractFactory.deploy() - const fs = cronReceiver1.interface.functions - handler1Sig = utils.id(fs['handler1()'].format('sighash')).slice(0, 10) - handler2Sig = utils.id(fs['handler2()'].format('sighash')).slice(0, 10) - revertHandlerSig = utils - .id(fs['revertHandler()'].format('sighash')) - .slice(0, 10) - const cronTHFactory = await ethers.getContractFactory( - 'CronInternalTestHelper', - ) - cronTestHelper = await cronTHFactory.deploy() - basicSpec = await cronFactoryContract.encodeCronString(basicCronString) - }) - - afterEach(async () => { - await h.reset() - }) - - it('has a limited public ABI [ @skip-coverage ]', () => { - // Casting cron is necessary due to a tricky versioning mismatch issue, likely between ethers - // and typechain. Remove once the version issue is resolved. - // https://smartcontract-it.atlassian.net/browse/ARCHIVE-22094 - h.publicAbi(cron as unknown as Contract, [ - 's_maxJobs', - 'performUpkeep', - 'createCronJobFromEncodedSpec', - 'updateCronJob', - 'deleteCronJob', - 'checkUpkeep', - 'getActiveCronJobIDs', - 'getCronJob', - // Ownable methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - // Pausable methods - 'paused', - 'pause', - 'unpause', - // Cron helper methods - 'createCronJobFromString', - 'txCheckUpkeep', - ]) - }) - - describe('constructor()', () => { - it('sets the initial values', async () => { - expect(await cron.owner()).to.equal(owner.address) - expect(await cron.s_maxJobs()).to.equal(5) - }) - - it('optionally creates a first job', async () => { - const payload = await cronFactoryContract.encodeCronJob( - cronReceiver1.address, - handler1Sig, - basicCronString, - ) - cron = ( - await cronFactory.deploy( - owner.address, - cronDelegate.address, - 5, - payload, - ) - ).connect(owner) - const job = await cron.getCronJob(1) - assert.equal(job.target, cronReceiver1.address) - assert.equal(job.handler, handler1Sig) - assert.equal(job.cronString, basicCronString) - }) - }) - - describe('checkUpkeep() / performUpkeep()', () => { - beforeEach(async () => { - await h.setTimestamp(timeStamp) - // id 1 - await cron.createCronJobFromString( - cronReceiver1.address, - handler1Sig, - '0 0 31 * *', // 31st day of every month - ) - // id 2 - await cron.createCronJobFromString( - cronReceiver1.address, - handler2Sig, - '10 * * * *', // on the 10 min mark - ) - // id 3 - await cron.createCronJobFromString( - cronReceiver2.address, - handler1Sig, - '0 0 * 7 *', // every day in July - ) - // id 4 - await cron.createCronJobFromString( - cronReceiver2.address, - revertHandlerSig, - '20 * * * *', // on the 20 min mark - ) - }) - - describe('checkUpkeep()', () => { - it('returns false if no one is elligible', async () => { - const [needsUpkeep] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - assert.isFalse(needsUpkeep) - }) - - it('returns the id of eligible cron jobs', async () => { - await h.fastForward(moment.duration(11, 'minutes').asSeconds()) - const [needsUpkeep, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - assert.isTrue(needsUpkeep) - const [id, ..._] = decodePayload(payload) - assert.equal(id.toNumber(), 2) - }) - - describe('when mutiple crons are elligible', () => { - it('cycles through the cron IDs based on block number', async () => { - await h.fastForward(moment.duration(1, 'year').asSeconds()) - let [_, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - const [id1] = decodePayload(payload) - await h.mineBlock(ethers.provider) - ;[_, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - const [id2] = decodePayload(payload) - await h.mineBlock(ethers.provider) - ;[_, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - const [id3] = decodePayload(payload) - await h.mineBlock(ethers.provider) - ;[_, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - const [id4] = decodePayload(payload) - assert.deepEqual( - [id1, id2, id3, id4].map((n) => n.toNumber()).sort(), - [1, 2, 3, 4], - ) - }) - }) - }) - - describe('performUpkeep()', () => { - it('forwards the call to the appropriate target/handler', async () => { - await h.fastForward(moment.duration(11, 'minutes').asSeconds()) - const [needsUpkeep, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - assert.isTrue(needsUpkeep) - await expect(cron.performUpkeep(payload)).to.emit( - cronReceiver1, - 'Received2', - ) - }) - - it('emits an event', async () => { - await h.fastForward(moment.duration(11, 'minutes').asSeconds()) - const [needsUpkeep, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - assert.isTrue(needsUpkeep) - await expect(cron.performUpkeep(payload)) - .to.emit(cron, 'CronJobExecuted') - .withArgs(2, true) - }) - - it('succeeds even if the call to the target fails', async () => { - await cron.deleteCronJob(2) - await h.fastForward(moment.duration(21, 'minutes').asSeconds()) - const payload = encodePayload([ - 4, - moment.unix(timeStamp).add(20, 'minutes').unix(), - cronReceiver2.address, - revertHandlerSig, - ]) - await expect(cron.performUpkeep(payload)) - .to.emit(cron, 'CronJobExecuted') - .withArgs(4, false) - }) - - it('is only callable by anyone', async () => { - await h.fastForward(moment.duration(11, 'minutes').asSeconds()) - const [needsUpkeep, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - assert.isTrue(needsUpkeep) - await cron.connect(stranger).performUpkeep(payload) - }) - - it('is only callable once for a given tick', async () => { - await h.fastForward(moment.duration(10, 'minutes').asSeconds()) - const [needsUpkeep, payload] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - assert.isTrue(needsUpkeep) - const maliciousPayload = encodePayload([ - 2, - moment.unix(timeStamp).add(10, 'minutes').add(59, 'seconds').unix(), - cronReceiver1.address, - handler2Sig, - ]) - await cron.performUpkeep(payload) - await expect(cron.performUpkeep(payload)).to.be.reverted - await expect(cron.performUpkeep(maliciousPayload)).to.be.reverted - await h.fastForward(moment.duration(1, 'minute').asSeconds()) - await expect(cron.performUpkeep(payload)).to.be.reverted - await expect(cron.performUpkeep(maliciousPayload)).to.be.reverted - await h.fastForward(moment.duration(10, 'minute').asSeconds()) - await expect(cron.performUpkeep(payload)).to.be.reverted - await expect(cron.performUpkeep(maliciousPayload)).to.be.reverted - }) - }) - }) - - describe('createCronJobFromEncodedSpec()', () => { - it('creates jobs with sequential IDs', async () => { - const cronString1 = '0 * * * *' - const cronString2 = '0 1,2,3 */4 5-6 1-2' - const encodedSpec1 = - await cronFactoryContract.encodeCronString(cronString1) - const encodedSpec2 = - await cronFactoryContract.encodeCronString(cronString2) - const nextTick1 = ( - await cronTestHelper.calculateNextTick(cronString1) - ).toNumber() - const nextTick2 = ( - await cronTestHelper.calculateNextTick(cronString2) - ).toNumber() - await cron.createCronJobFromEncodedSpec( - cronReceiver1.address, - handler1Sig, - encodedSpec1, - ) - await assertJobIDsEqual([1]) - await cron.createCronJobFromEncodedSpec( - cronReceiver1.address, - handler2Sig, - encodedSpec1, - ) - await assertJobIDsEqual([1, 2]) - await cron.createCronJobFromEncodedSpec( - cronReceiver2.address, - handler1Sig, - encodedSpec2, - ) - await assertJobIDsEqual([1, 2, 3]) - await cron.createCronJobFromEncodedSpec( - cronReceiver2.address, - handler2Sig, - encodedSpec2, - ) - await assertJobIDsEqual([1, 2, 3, 4]) - const cron1 = await cron.getCronJob(1) - const cron2 = await cron.getCronJob(2) - const cron3 = await cron.getCronJob(3) - const cron4 = await cron.getCronJob(4) - assert.equal(cron1.target, cronReceiver1.address) - assert.equal(cron1.handler, handler1Sig) - assert.equal(cron1.cronString, cronString1) - assert.equal(cron1.nextTick.toNumber(), nextTick1) - assert.equal(cron2.target, cronReceiver1.address) - assert.equal(cron2.handler, handler2Sig) - assert.equal(cron2.cronString, cronString1) - assert.equal(cron2.nextTick.toNumber(), nextTick1) - assert.equal(cron3.target, cronReceiver2.address) - assert.equal(cron3.handler, handler1Sig) - assert.equal(cron3.cronString, cronString2) - assert.equal(cron3.nextTick.toNumber(), nextTick2) - assert.equal(cron4.target, cronReceiver2.address) - assert.equal(cron4.handler, handler2Sig) - assert.equal(cron4.cronString, cronString2) - assert.equal(cron4.nextTick.toNumber(), nextTick2) - }) - - it('emits an event', async () => { - await expect(createBasicCron()).to.emit(cron, 'CronJobCreated') - }) - - it('is only callable by the owner', async () => { - await expect( - cron - .connect(stranger) - .createCronJobFromEncodedSpec( - cronReceiver1.address, - handler1Sig, - basicSpec, - ), - ).to.be.revertedWith(OWNABLE_ERR) - }) - - it('errors if trying to create more jobs than allowed', async () => { - for (let idx = 0; idx < 5; idx++) { - await createBasicCron() - } - await expect(createBasicCron()).to.be.revertedWithCustomError( - cron, - 'ExceedsMaxJobs', - ) - }) - }) - - describe('updateCronJob()', () => { - const newCronString = '0 0 1 1 1' - let newEncodedSpec: string - beforeEach(async () => { - await createBasicCron() - newEncodedSpec = await cronFactoryContract.encodeCronString(newCronString) - }) - - it('updates a cron job', async () => { - let cron1 = await cron.getCronJob(1) - assert.equal(cron1.target, cronReceiver1.address) - assert.equal(cron1.handler, handler1Sig) - assert.equal(cron1.cronString, basicCronString) - await cron.updateCronJob( - 1, - cronReceiver2.address, - handler2Sig, - newEncodedSpec, - ) - cron1 = await cron.getCronJob(1) - assert.equal(cron1.target, cronReceiver2.address) - assert.equal(cron1.handler, handler2Sig) - assert.equal(cron1.cronString, newCronString) - }) - - it('emits an event', async () => { - await expect( - await cron.updateCronJob( - 1, - cronReceiver2.address, - handler2Sig, - newEncodedSpec, - ), - ).to.emit(cron, 'CronJobUpdated') - }) - - it('is only callable by the owner', async () => { - await expect( - cron - .connect(stranger) - .updateCronJob(1, cronReceiver2.address, handler2Sig, newEncodedSpec), - ).to.be.revertedWith(OWNABLE_ERR) - }) - - it('reverts if trying to update a non-existent ID', async () => { - await expect( - cron.updateCronJob( - 2, - cronReceiver2.address, - handler2Sig, - newEncodedSpec, - ), - ).to.be.revertedWithCustomError(cron, CRON_NOT_FOUND_ERR) - }) - }) - - describe('deleteCronJob()', () => { - it("deletes a jobs by it's ID", async () => { - await createBasicCron() - await createBasicCron() - await createBasicCron() - await createBasicCron() - await assertJobIDsEqual([1, 2, 3, 4]) - await cron.deleteCronJob(2) - await expect(cron.getCronJob(2)).to.be.revertedWithCustomError( - cron, - CRON_NOT_FOUND_ERR, - ) - await expect(cron.deleteCronJob(2)).to.be.revertedWithCustomError( - cron, - CRON_NOT_FOUND_ERR, - ) - await assertJobIDsEqual([1, 3, 4]) - await cron.deleteCronJob(1) - await assertJobIDsEqual([3, 4]) - await cron.deleteCronJob(4) - await assertJobIDsEqual([3]) - await cron.deleteCronJob(3) - await assertJobIDsEqual([]) - }) - - it('emits an event', async () => { - await createBasicCron() - await expect(cron.deleteCronJob(1)).to.emit(cron, 'CronJobDeleted') - }) - - it('reverts if trying to delete a non-existent ID', async () => { - await createBasicCron() - await createBasicCron() - await expect(cron.deleteCronJob(0)).to.be.revertedWithCustomError( - cron, - CRON_NOT_FOUND_ERR, - ) - await expect(cron.deleteCronJob(3)).to.be.revertedWithCustomError( - cron, - CRON_NOT_FOUND_ERR, - ) - }) - }) - - describe('pause() / unpause()', () => { - it('is only callable by the owner', async () => { - await expect(cron.connect(stranger).pause()).to.be.reverted - await expect(cron.connect(stranger).unpause()).to.be.reverted - }) - - it('pauses / unpauses the contract', async () => { - expect(await cron.paused()).to.be.false - await cron.pause() - expect(await cron.paused()).to.be.true - await cron.unpause() - expect(await cron.paused()).to.be.false - }) - }) -}) - -// only run during pnpm test:gas -describe.skip('Cron Gas Usage', () => { - before(async () => { - const accounts = await ethers.getSigners() - admin = accounts[0] - owner = accounts[1] - const crFactory = await ethers.getContractFactory('CronReceiver', owner) - cronReceiver1 = await crFactory.deploy() - const cronDelegateFactory = await ethers.getContractFactory( - 'CronUpkeepDelegate', - owner, - ) - const cronDelegate = await cronDelegateFactory.deploy() - const cronExternalFactory = await ethers.getContractFactory( - 'src/v0.8/automation/libraries/external/Cron.sol:Cron', - admin, - ) - const cronExternalLib = await cronExternalFactory.deploy() - const cronFactory = await ethers.getContractFactory( - 'CronUpkeepTestHelper', - { - signer: owner, - libraries: { Cron: cronExternalLib.address }, - }, - ) - cron = await cronFactory.deploy(owner.address, cronDelegate.address, 5, []) - const fs = cronReceiver1.interface.functions - handler1Sig = utils - .id(fs['handler1()'].format('sighash')) // TODO this seems like an ethers bug - .slice(0, 10) - }) - - describe('checkUpkeep() / performUpkeep()', () => { - it('uses gas', async () => { - for (let idx = 0; idx < validCrons.length; idx++) { - const cronString = validCrons[idx] - const cronID = idx + 1 - await cron.createCronJobFromString( - cronReceiver1.address, - handler1Sig, - cronString, - ) - await h.fastForward(moment.duration(100, 'years').asSeconds()) // long enough that at least 1 tick occurs - const [needsUpkeep, data] = await cron - .connect(AddressZero) - .callStatic.checkUpkeep('0x') - assert.isTrue(needsUpkeep, `failed for cron string ${cronString}`) - await cron.txCheckUpkeep('0x') - await cron.performUpkeep(data) - await cron.deleteCronJob(cronID) - } - }) - }) -}) diff --git a/contracts/test/v0.8/automation/CronUpkeepFactory.test.ts b/contracts/test/v0.8/automation/CronUpkeepFactory.test.ts deleted file mode 100644 index e9a7de837b7..00000000000 --- a/contracts/test/v0.8/automation/CronUpkeepFactory.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { ethers } from 'hardhat' -import { Contract } from 'ethers' -import { assert, expect } from 'chai' -import { CronUpkeepFactory } from '../../../typechain/CronUpkeepFactory' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import * as h from '../../test-helpers/helpers' -import { reset } from '../../test-helpers/helpers' - -const OWNABLE_ERR = 'Only callable by owner' - -let cronExternalLib: Contract -let factory: CronUpkeepFactory - -let admin: SignerWithAddress -let owner: SignerWithAddress -let stranger: SignerWithAddress - -describe('CronUpkeepFactory', () => { - beforeEach(async () => { - const accounts = await ethers.getSigners() - admin = accounts[0] - owner = accounts[1] - stranger = accounts[2] - const cronExternalFactory = await ethers.getContractFactory( - 'src/v0.8/automation/libraries/external/Cron.sol:Cron', - admin, - ) - cronExternalLib = await cronExternalFactory.deploy() - const cronUpkeepFactoryFactory = await ethers.getContractFactory( - 'CronUpkeepFactory', - { - signer: admin, - libraries: { - Cron: cronExternalLib.address, - }, - }, - ) - factory = await cronUpkeepFactoryFactory.deploy() - }) - - afterEach(async () => { - await reset() - }) - - it('has a limited public ABI [ @skip-coverage ]', () => { - h.publicAbi(factory as unknown as Contract, [ - 's_maxJobs', - 'newCronUpkeep', - 'newCronUpkeepWithJob', - 'setMaxJobs', - 'cronDelegateAddress', - 'encodeCronString', - 'encodeCronJob', - // Ownable methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('constructor()', () => { - it('deploys a delegate contract', async () => { - assert.notEqual( - await factory.cronDelegateAddress(), - ethers.constants.AddressZero, - ) - }) - }) - - describe('newCronUpkeep()', () => { - it('emits an event', async () => { - await expect(factory.connect(owner).newCronUpkeep()).to.emit( - factory, - 'NewCronUpkeepCreated', - ) - }) - it('sets the deployer as the owner', async () => { - const response = await factory.connect(owner).newCronUpkeep() - const { events } = await response.wait() - if (!events) { - assert.fail('no events emitted') - } - const upkeepAddress = events[0].args?.upkeep - const cronUpkeepFactory = await ethers.getContractFactory('CronUpkeep', { - libraries: { Cron: cronExternalLib.address }, - }) - assert( - await cronUpkeepFactory.attach(upkeepAddress).owner(), - owner.address, - ) - }) - }) - - describe('setMaxJobs()', () => { - it('sets the max jobs value', async () => { - expect(await factory.s_maxJobs()).to.equal(5) - await factory.setMaxJobs(6) - expect(await factory.s_maxJobs()).to.equal(6) - }) - - it('is only callable by the owner', async () => { - await expect(factory.connect(stranger).setMaxJobs(6)).to.be.revertedWith( - OWNABLE_ERR, - ) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/ERC20BalanceMonitor.test.ts b/contracts/test/v0.8/automation/ERC20BalanceMonitor.test.ts deleted file mode 100644 index 2d5d113abca..00000000000 --- a/contracts/test/v0.8/automation/ERC20BalanceMonitor.test.ts +++ /dev/null @@ -1,695 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { ReceiveEmitter } from '../../../typechain/ReceiveEmitter' -import { ReceiveFallbackEmitter } from '../../../typechain/ReceiveFallbackEmitter' -import * as h from '../../test-helpers/helpers' -import { ERC20BalanceMonitorExposed, LinkToken } from '../../../typechain' -import { BigNumber } from 'ethers' - -const OWNABLE_ERR = 'Only callable by owner' -const INVALID_WATCHLIST_ERR = `InvalidWatchList` -const PAUSED_ERR = 'Pausable: paused' -const ONLY_KEEPER_ERR = `OnlyKeeperRegistry` - -const zeroLINK = ethers.utils.parseEther('0') -const oneLINK = ethers.utils.parseEther('1') -const twoLINK = ethers.utils.parseEther('2') -const threeLINK = ethers.utils.parseEther('3') -const fiveLINK = ethers.utils.parseEther('5') -const sixLINK = ethers.utils.parseEther('6') -const tenLINK = ethers.utils.parseEther('10') - -const oneHundredLINK = ethers.utils.parseEther('100') - -const watchAddress1 = ethers.Wallet.createRandom().address -const watchAddress2 = ethers.Wallet.createRandom().address -const watchAddress3 = ethers.Wallet.createRandom().address -const watchAddress4 = ethers.Wallet.createRandom().address -let watchAddress5: string -let watchAddress6: string - -let bm: ERC20BalanceMonitorExposed -let lt: LinkToken -let receiveEmitter: ReceiveEmitter -let receiveFallbackEmitter: ReceiveFallbackEmitter -let owner: SignerWithAddress -let stranger: SignerWithAddress -let keeperRegistry: SignerWithAddress - -async function assertWatchlistBalances( - balance1: BigNumber, - balance2: BigNumber, - balance3: BigNumber, - balance4: BigNumber, - balance5: BigNumber, - balance6: BigNumber, -) { - await h.assertLinkTokenBalance(lt, watchAddress1, balance1, 'address 1') - await h.assertLinkTokenBalance(lt, watchAddress2, balance2, 'address 2') - await h.assertLinkTokenBalance(lt, watchAddress3, balance3, 'address 3') - await h.assertLinkTokenBalance(lt, watchAddress4, balance4, 'address 4') - await h.assertLinkTokenBalance(lt, watchAddress5, balance5, 'address 5') - await h.assertLinkTokenBalance(lt, watchAddress6, balance6, 'address 6') -} - -describe('ERC20BalanceMonitor', () => { - beforeEach(async () => { - const accounts = await ethers.getSigners() - owner = accounts[0] - stranger = accounts[1] - keeperRegistry = accounts[2] - watchAddress5 = accounts[3].address - watchAddress6 = accounts[4].address - - const bmFactory = await ethers.getContractFactory( - 'ERC20BalanceMonitorExposed', - owner, - ) - const ltFactory = await ethers.getContractFactory( - 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', - owner, - ) - const reFactory = await ethers.getContractFactory('ReceiveEmitter', owner) - const rfeFactory = await ethers.getContractFactory( - 'ReceiveFallbackEmitter', - owner, - ) - - lt = await ltFactory.deploy() - bm = await bmFactory.deploy(lt.address, keeperRegistry.address, 0) - - for (let i = 1; i <= 4; i++) { - const recipient = await accounts[i].getAddress() - await lt.connect(owner).transfer(recipient, oneHundredLINK) - } - - receiveEmitter = await reFactory.deploy() - receiveFallbackEmitter = await rfeFactory.deploy() - await Promise.all([ - bm.deployed(), - receiveEmitter.deployed(), - receiveFallbackEmitter.deployed(), - ]) - }) - - afterEach(async () => { - await h.reset() - }) - - describe('add funds', () => { - it('Should allow anyone to add funds', async () => { - await lt.transfer(bm.address, oneLINK) - await lt.connect(stranger).transfer(bm.address, oneLINK) - }) - }) - - describe('withdraw()', () => { - beforeEach(async () => { - const tx = await lt.connect(owner).transfer(bm.address, oneLINK) - await tx.wait() - }) - - it('Should allow the owner to withdraw', async () => { - const beforeBalance = await lt.balanceOf(owner.address) - const tx = await bm.connect(owner).withdraw(oneLINK, owner.address) - await tx.wait() - const afterBalance = await lt.balanceOf(owner.address) - assert.isTrue( - afterBalance.gt(beforeBalance), - 'balance did not increase after withdraw', - ) - }) - - it('Should emit an event', async () => { - const tx = await bm.connect(owner).withdraw(oneLINK, owner.address) - await expect(tx) - .to.emit(bm, 'FundsWithdrawn') - .withArgs(oneLINK, owner.address) - }) - - it('Should allow the owner to withdraw to anyone', async () => { - const beforeBalance = await lt.balanceOf(stranger.address) - const tx = await bm.connect(owner).withdraw(oneLINK, stranger.address) - await tx.wait() - const afterBalance = await lt.balanceOf(stranger.address) - assert.isTrue( - beforeBalance.add(oneLINK).eq(afterBalance), - 'balance did not increase after withdraw', - ) - }) - - it('Should not allow strangers to withdraw', async () => { - const tx = bm.connect(stranger).withdraw(oneLINK, owner.address) - await expect(tx).to.be.revertedWith(OWNABLE_ERR) - }) - }) - - describe('pause() / unpause()', () => { - it('Should allow owner to pause / unpause', async () => { - const pauseTx = await bm.connect(owner).pause() - await pauseTx.wait() - const unpauseTx = await bm.connect(owner).unpause() - await unpauseTx.wait() - }) - - it('Should not allow strangers to pause / unpause', async () => { - const pauseTxStranger = bm.connect(stranger).pause() - await expect(pauseTxStranger).to.be.revertedWith(OWNABLE_ERR) - const pauseTxOwner = await bm.connect(owner).pause() - await pauseTxOwner.wait() - const unpauseTxStranger = bm.connect(stranger).unpause() - await expect(unpauseTxStranger).to.be.revertedWith(OWNABLE_ERR) - }) - }) - - describe('setWatchList() / getWatchList() / getAccountInfo()', () => { - it('Should allow owner to set the watchlist', async () => { - // should start unactive - assert.isFalse((await bm.getAccountInfo(watchAddress1)).isActive) - // add first watchlist - let setTx = await bm - .connect(owner) - .setWatchList([watchAddress1], [oneLINK], [twoLINK]) - await setTx.wait() - let watchList = await bm.getWatchList() - assert.deepEqual(watchList, [watchAddress1]) - const accountInfo = await bm.getAccountInfo(watchAddress1) - assert.isTrue(accountInfo.isActive) - expect(accountInfo.minBalance).to.equal(oneLINK) - expect(accountInfo.topUpLevel).to.equal(twoLINK) - // add more to watchlist - setTx = await bm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2, watchAddress3], - [oneLINK, twoLINK, threeLINK], - [twoLINK, threeLINK, fiveLINK], - ) - await setTx.wait() - watchList = await bm.getWatchList() - assert.deepEqual(watchList, [watchAddress1, watchAddress2, watchAddress3]) - let accountInfo1 = await bm.getAccountInfo(watchAddress1) - let accountInfo2 = await bm.getAccountInfo(watchAddress2) - let accountInfo3 = await bm.getAccountInfo(watchAddress3) - expect(accountInfo1.isActive).to.be.true - expect(accountInfo1.minBalance).to.equal(oneLINK) - expect(accountInfo1.topUpLevel).to.equal(twoLINK) - expect(accountInfo2.isActive).to.be.true - expect(accountInfo2.minBalance).to.equal(twoLINK) - expect(accountInfo2.topUpLevel).to.equal(threeLINK) - expect(accountInfo3.isActive).to.be.true - expect(accountInfo3.minBalance).to.equal(threeLINK) - expect(accountInfo3.topUpLevel).to.equal(fiveLINK) - // remove some from watchlist - setTx = await bm - .connect(owner) - .setWatchList( - [watchAddress3, watchAddress1], - [threeLINK, oneLINK], - [fiveLINK, twoLINK], - ) - await setTx.wait() - watchList = await bm.getWatchList() - assert.deepEqual(watchList, [watchAddress3, watchAddress1]) - accountInfo1 = await bm.getAccountInfo(watchAddress1) - accountInfo2 = await bm.getAccountInfo(watchAddress2) - accountInfo3 = await bm.getAccountInfo(watchAddress3) - expect(accountInfo1.isActive).to.be.true - expect(accountInfo2.isActive).to.be.false - expect(accountInfo3.isActive).to.be.true - }) - - it('Should not allow duplicates in the watchlist', async () => { - const errMsg = `DuplicateAddress` - const setTx = bm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2, watchAddress1], - [oneLINK, twoLINK, threeLINK], - [twoLINK, threeLINK, fiveLINK], - ) - await expect(setTx) - .to.be.revertedWithCustomError(bm, errMsg) - .withArgs(watchAddress1) - }) - - it('Should not allow a topUpLevel les than or equal to minBalance in the watchlist', async () => { - const setTx = bm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2, watchAddress1], - [oneLINK, twoLINK, threeLINK], - [zeroLINK, twoLINK, threeLINK], - ) - await expect(setTx).to.be.revertedWithCustomError( - bm, - INVALID_WATCHLIST_ERR, - ) - }) - - it('Should not allow larger than maximum watchlist size', async () => { - const watchlist: any[][] = [[], [], []] - Array.from(Array(301).keys()).forEach(() => { - watchlist[0].push(owner.address) - watchlist[1].push(oneLINK) - watchlist[2].push(twoLINK) - }) - const tx = bm - .connect(owner) - .setWatchList(watchlist[0], watchlist[1], watchlist[2]) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - }) - - it('Should not allow strangers to set the watchlist', async () => { - const setTxStranger = bm - .connect(stranger) - .setWatchList([watchAddress1], [oneLINK], [twoLINK]) - await expect(setTxStranger).to.be.revertedWith(OWNABLE_ERR) - }) - - it('Should revert if the list lengths differ', async () => { - let tx = bm.connect(owner).setWatchList([watchAddress1], [], [twoLINK]) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - tx = bm.connect(owner).setWatchList([watchAddress1], [oneLINK], []) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - tx = bm.connect(owner).setWatchList([], [oneLINK], [twoLINK]) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - }) - - it('Should revert if any of the addresses are empty', async () => { - let tx = bm - .connect(owner) - .setWatchList( - [watchAddress1, ethers.constants.AddressZero], - [oneLINK, oneLINK], - [twoLINK, twoLINK], - ) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - }) - - it('Should revert if any of the top up amounts are 0', async () => { - const tx = bm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2], - [oneLINK, oneLINK], - [twoLINK, zeroLINK], - ) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - }) - }) - - describe('getKeeperRegistryAddress() / setKeeperRegistryAddress()', () => { - const newAddress = ethers.Wallet.createRandom().address - - it('Should initialize with the registry address provided to the constructor', async () => { - const address = await bm.getKeeperRegistryAddress() - assert.equal(address, keeperRegistry.address) - }) - - it('Should allow the owner to set the registry address', async () => { - const setTx = await bm.connect(owner).setKeeperRegistryAddress(newAddress) - await setTx.wait() - const address = await bm.getKeeperRegistryAddress() - assert.equal(address, newAddress) - }) - - it('Should not allow strangers to set the registry address', async () => { - const setTx = bm.connect(stranger).setKeeperRegistryAddress(newAddress) - await expect(setTx).to.be.revertedWith(OWNABLE_ERR) - }) - - it('Should emit an event', async () => { - const setTx = await bm.connect(owner).setKeeperRegistryAddress(newAddress) - await expect(setTx) - .to.emit(bm, 'KeeperRegistryAddressUpdated') - .withArgs(keeperRegistry.address, newAddress) - }) - }) - - describe('getMinWaitPeriodSeconds / setMinWaitPeriodSeconds()', () => { - const newWaitPeriod = BigNumber.from(1) - - it('Should initialize with the wait period provided to the constructor', async () => { - const minWaitPeriod = await bm.getMinWaitPeriodSeconds() - expect(minWaitPeriod).to.equal(0) - }) - - it('Should allow owner to set the wait period', async () => { - const setTx = await bm - .connect(owner) - .setMinWaitPeriodSeconds(newWaitPeriod) - await setTx.wait() - const minWaitPeriod = await bm.getMinWaitPeriodSeconds() - expect(minWaitPeriod).to.equal(newWaitPeriod) - }) - - it('Should not allow strangers to set the wait period', async () => { - const setTx = bm.connect(stranger).setMinWaitPeriodSeconds(newWaitPeriod) - await expect(setTx).to.be.revertedWith(OWNABLE_ERR) - }) - - it('Should emit an event', async () => { - const setTx = await bm - .connect(owner) - .setMinWaitPeriodSeconds(newWaitPeriod) - await expect(setTx) - .to.emit(bm, 'MinWaitPeriodUpdated') - .withArgs(0, newWaitPeriod) - }) - }) - - describe('checkUpkeep() / getUnderfundedAddresses()', () => { - beforeEach(async () => { - const setTx = await bm.connect(owner).setWatchList( - [ - watchAddress1, // needs funds - watchAddress5, // funded - watchAddress2, // needs funds - watchAddress6, // funded - watchAddress3, // needs funds - ], - new Array(5).fill(oneLINK), - new Array(5).fill(twoLINK), - ) - await setTx.wait() - }) - - it('Should return list of address that are underfunded', async () => { - const fundTx = await lt.connect(owner).transfer( - bm.address, - sixLINK, // needs 6 total - ) - await fundTx.wait() - const [should, payload] = await bm.checkUpkeep('0x') - assert.isTrue(should) - let [addresses] = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - payload, - ) - assert.deepEqual(addresses, [watchAddress1, watchAddress2, watchAddress3]) - // checkUpkeep payload should match getUnderfundedAddresses() - addresses = await bm.getUnderfundedAddresses() - assert.deepEqual(addresses, [watchAddress1, watchAddress2, watchAddress3]) - }) - - it('Should return some results even if contract cannot fund all eligible targets', async () => { - const fundTx = await lt.connect(owner).transfer( - bm.address, - fiveLINK, // needs 6 total - ) - await fundTx.wait() - const [should, payload] = await bm.checkUpkeep('0x') - assert.isTrue(should) - const [addresses] = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - payload, - ) - assert.deepEqual(addresses, [watchAddress1, watchAddress2]) - }) - - it('Should omit addresses that have been funded recently', async () => { - const setWaitPdTx = await bm.setMinWaitPeriodSeconds(3600) // 1 hour - const fundTx = await lt.connect(owner).transfer(bm.address, sixLINK) - await Promise.all([setWaitPdTx.wait(), fundTx.wait()]) - const block = await ethers.provider.getBlock('latest') - const setTopUpTx = await bm.setLastTopUpXXXTestOnly( - watchAddress2, - block.timestamp - 100, - ) - await setTopUpTx.wait() - const [should, payload] = await bm.checkUpkeep('0x') - assert.isTrue(should) - const [addresses] = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - payload, - ) - assert.deepEqual(addresses, [watchAddress1, watchAddress3]) - }) - - it('Should revert when paused', async () => { - const tx = await bm.connect(owner).pause() - await tx.wait() - const ethCall = bm.checkUpkeep('0x') - await expect(ethCall).to.be.revertedWith(PAUSED_ERR) - }) - }) - - describe('performUpkeep()', () => { - let validPayload: string - let invalidPayload: string - - beforeEach(async () => { - validPayload = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [[watchAddress1, watchAddress2, watchAddress3]], - ) - invalidPayload = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [[watchAddress1, watchAddress2, watchAddress4, watchAddress5]], - ) - const setTx = await bm.connect(owner).setWatchList( - [ - watchAddress1, // needs funds - watchAddress5, // funded - watchAddress2, // needs funds - watchAddress6, // funded - watchAddress3, // needs funds - // watchAddress4 - omitted - ], - new Array(5).fill(oneLINK), - new Array(5).fill(twoLINK), - ) - await setTx.wait() - }) - - it('Should revert when paused', async () => { - const pauseTx = await bm.connect(owner).pause() - await pauseTx.wait() - const performTx = bm.connect(keeperRegistry).performUpkeep(validPayload) - await expect(performTx).to.be.revertedWith(PAUSED_ERR) - }) - - context('when partially funded', () => { - it('Should fund as many addresses as possible', async () => { - const fundTx = await lt.connect(owner).transfer( - bm.address, - fiveLINK, // only enough LINK to fund 2 addresses - ) - await fundTx.wait() - await assertWatchlistBalances( - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(validPayload) - await assertWatchlistBalances( - twoLINK, - twoLINK, - zeroLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(watchAddress1) - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(watchAddress2) - }) - }) - - context('when fully funded', () => { - beforeEach(async () => { - const fundTx = await lt.connect(owner).transfer(bm.address, tenLINK) - await fundTx.wait() - }) - - it('Should fund the appropriate addresses', async () => { - await assertWatchlistBalances( - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(validPayload, { gasLimit: 2_500_000 }) - await performTx.wait() - await assertWatchlistBalances( - twoLINK, - twoLINK, - twoLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - }) - - it('Should only fund active, underfunded addresses', async () => { - await assertWatchlistBalances( - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(invalidPayload, { gasLimit: 2_500_000 }) - await performTx.wait() - await assertWatchlistBalances( - twoLINK, - twoLINK, - zeroLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - }) - - it('Should not fund addresses that have been funded recently', async () => { - const setWaitPdTx = await bm.setMinWaitPeriodSeconds(3600) // 1 hour - await setWaitPdTx.wait() - const block = await ethers.provider.getBlock('latest') - const setTopUpTx = await bm.setLastTopUpXXXTestOnly( - watchAddress2, - block.timestamp - 100, - ) - await setTopUpTx.wait() - await assertWatchlistBalances( - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(validPayload, { gasLimit: 2_500_000 }) - await performTx.wait() - await assertWatchlistBalances( - twoLINK, - zeroLINK, - twoLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - }) - - it('Should only be callable by the keeper registry contract', async () => { - let performTx = bm.connect(owner).performUpkeep(validPayload) - await expect(performTx).to.be.revertedWithCustomError( - bm, - ONLY_KEEPER_ERR, - ) - performTx = bm.connect(stranger).performUpkeep(validPayload) - await expect(performTx).to.be.revertedWithCustomError( - bm, - ONLY_KEEPER_ERR, - ) - }) - - it('Should protect against running out of gas', async () => { - await assertWatchlistBalances( - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - oneHundredLINK, - oneHundredLINK, - ) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(validPayload, { gasLimit: 130_000 }) // too little for all 3 transfers - await performTx.wait() - const balance1 = await lt.balanceOf(watchAddress1) - const balance2 = await lt.balanceOf(watchAddress2) - const balance3 = await lt.balanceOf(watchAddress3) - const balances = [balance1, balance2, balance3].map((n) => n.toString()) - expect(balances) - .to.include(twoLINK.toString()) // expect at least 1 transfer - .to.include(zeroLINK.toString()) // expect at least 1 out of funds - }) - - it('Should provide enough gas to support receive and fallback functions', async () => { - const addresses = [ - receiveEmitter.address, - receiveFallbackEmitter.address, - ] - const payload = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [addresses], - ) - const setTx = await bm - .connect(owner) - .setWatchList( - addresses, - new Array(2).fill(oneLINK), - new Array(2).fill(twoLINK), - ) - await setTx.wait() - - const reBalanceBefore = await lt.balanceOf(receiveEmitter.address) - const rfeBalanceBefore = await lt.balanceOf( - receiveFallbackEmitter.address, - ) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(payload, { gasLimit: 2_500_000 }) - await h.assertLinkTokenBalance( - lt, - receiveEmitter.address, - reBalanceBefore.add(twoLINK), - ) - await h.assertLinkTokenBalance( - lt, - receiveFallbackEmitter.address, - rfeBalanceBefore.add(twoLINK), - ) - - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(receiveEmitter.address) - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(receiveFallbackEmitter.address) - }) - }) - }) - - describe('topUp()', () => { - context('when not paused', () => { - it('Should be callable by anyone', async () => { - const users = [owner, keeperRegistry, stranger] - for (let idx = 0; idx < users.length; idx++) { - const user = users[idx] - await bm.connect(user).topUp([]) - } - }) - }) - context('when paused', () => { - it('Should be callable by no one', async () => { - await bm.connect(owner).pause() - const users = [owner, keeperRegistry, stranger] - for (let idx = 0; idx < users.length; idx++) { - const user = users[idx] - const tx = bm.connect(user).topUp([]) - await expect(tx).to.be.revertedWith(PAUSED_ERR) - } - }) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/EthBalanceMonitor.test.ts b/contracts/test/v0.8/automation/EthBalanceMonitor.test.ts deleted file mode 100644 index edcf1b564c9..00000000000 --- a/contracts/test/v0.8/automation/EthBalanceMonitor.test.ts +++ /dev/null @@ -1,663 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { EthBalanceMonitorExposed } from '../../../typechain/EthBalanceMonitorExposed' -import { ReceiveReverter } from '../../../typechain/ReceiveReverter' -import { ReceiveEmitter } from '../../../typechain/ReceiveEmitter' -import { ReceiveFallbackEmitter } from '../../../typechain/ReceiveFallbackEmitter' -import { BigNumber } from 'ethers' -import * as h from '../../test-helpers/helpers' - -const OWNABLE_ERR = 'Only callable by owner' -const INVALID_WATCHLIST_ERR = `InvalidWatchList` -const PAUSED_ERR = 'Pausable: paused' -const ONLY_KEEPER_ERR = `OnlyKeeperRegistry` - -const zeroEth = ethers.utils.parseEther('0') -const oneEth = ethers.utils.parseEther('1') -const twoEth = ethers.utils.parseEther('2') -const threeEth = ethers.utils.parseEther('3') -const fiveEth = ethers.utils.parseEther('5') -const sixEth = ethers.utils.parseEther('6') -const tenEth = ethers.utils.parseEther('10') - -const watchAddress1 = ethers.Wallet.createRandom().address -const watchAddress2 = ethers.Wallet.createRandom().address -const watchAddress3 = ethers.Wallet.createRandom().address -const watchAddress4 = ethers.Wallet.createRandom().address -let watchAddress5: string -let watchAddress6: string - -async function assertWatchlistBalances( - balance1: number, - balance2: number, - balance3: number, - balance4: number, - balance5: number, - balance6: number, -) { - const toEth = (n: number) => ethers.utils.parseUnits(n.toString(), 'ether') - await h.assertBalance(watchAddress1, toEth(balance1), 'address 1') - await h.assertBalance(watchAddress2, toEth(balance2), 'address 2') - await h.assertBalance(watchAddress3, toEth(balance3), 'address 3') - await h.assertBalance(watchAddress4, toEth(balance4), 'address 4') - await h.assertBalance(watchAddress5, toEth(balance5), 'address 5') - await h.assertBalance(watchAddress6, toEth(balance6), 'address 6') -} - -let bm: EthBalanceMonitorExposed -let receiveReverter: ReceiveReverter -let receiveEmitter: ReceiveEmitter -let receiveFallbackEmitter: ReceiveFallbackEmitter -let owner: SignerWithAddress -let stranger: SignerWithAddress -let keeperRegistry: SignerWithAddress - -describe('EthBalanceMonitor', () => { - beforeEach(async () => { - const accounts = await ethers.getSigners() - owner = accounts[0] - stranger = accounts[1] - keeperRegistry = accounts[2] - watchAddress5 = accounts[3].address - watchAddress6 = accounts[4].address - - const bmFactory = await ethers.getContractFactory( - 'EthBalanceMonitorExposed', - owner, - ) - const rrFactory = await ethers.getContractFactory('ReceiveReverter', owner) - const reFactory = await ethers.getContractFactory('ReceiveEmitter', owner) - const rfeFactory = await ethers.getContractFactory( - 'ReceiveFallbackEmitter', - owner, - ) - - bm = await bmFactory.deploy(keeperRegistry.address, 0) - receiveReverter = await rrFactory.deploy() - receiveEmitter = await reFactory.deploy() - receiveFallbackEmitter = await rfeFactory.deploy() - await Promise.all([ - bm.deployed(), - receiveReverter.deployed(), - receiveEmitter.deployed(), - receiveFallbackEmitter.deployed(), - ]) - }) - - afterEach(async () => { - await h.reset() - }) - - describe('receive()', () => { - it('Should allow anyone to add funds', async () => { - await owner.sendTransaction({ - to: bm.address, - value: oneEth, - }) - await stranger.sendTransaction({ - to: bm.address, - value: oneEth, - }) - }) - - it('Should emit an event', async () => { - await owner.sendTransaction({ - to: bm.address, - value: oneEth, - }) - const tx = stranger.sendTransaction({ - to: bm.address, - value: oneEth, - }) - await expect(tx) - .to.emit(bm, 'FundsAdded') - .withArgs(oneEth, twoEth, stranger.address) - }) - }) - - describe('withdraw()', () => { - beforeEach(async () => { - const tx = await owner.sendTransaction({ - to: bm.address, - value: oneEth, - }) - await tx.wait() - }) - - it('Should allow the owner to withdraw', async () => { - const beforeBalance = await owner.getBalance() - const tx = await bm.connect(owner).withdraw(oneEth, owner.address) - await tx.wait() - const afterBalance = await owner.getBalance() - assert.isTrue( - afterBalance.gt(beforeBalance), - 'balance did not increase after withdraw', - ) - }) - - it('Should emit an event', async () => { - const tx = await bm.connect(owner).withdraw(oneEth, owner.address) - await expect(tx) - .to.emit(bm, 'FundsWithdrawn') - .withArgs(oneEth, owner.address) - }) - - it('Should allow the owner to withdraw to anyone', async () => { - const beforeBalance = await stranger.getBalance() - const tx = await bm.connect(owner).withdraw(oneEth, stranger.address) - await tx.wait() - const afterBalance = await stranger.getBalance() - assert.isTrue( - beforeBalance.add(oneEth).eq(afterBalance), - 'balance did not increase after withdraw', - ) - }) - - it('Should not allow strangers to withdraw', async () => { - const tx = bm.connect(stranger).withdraw(oneEth, owner.address) - await expect(tx).to.be.revertedWith(OWNABLE_ERR) - }) - }) - - describe('pause() / unpause()', () => { - it('Should allow owner to pause / unpause', async () => { - const pauseTx = await bm.connect(owner).pause() - await pauseTx.wait() - const unpauseTx = await bm.connect(owner).unpause() - await unpauseTx.wait() - }) - - it('Should not allow strangers to pause / unpause', async () => { - const pauseTxStranger = bm.connect(stranger).pause() - await expect(pauseTxStranger).to.be.revertedWith(OWNABLE_ERR) - const pauseTxOwner = await bm.connect(owner).pause() - await pauseTxOwner.wait() - const unpauseTxStranger = bm.connect(stranger).unpause() - await expect(unpauseTxStranger).to.be.revertedWith(OWNABLE_ERR) - }) - }) - - describe('setWatchList() / getWatchList() / getAccountInfo()', () => { - it('Should allow owner to set the watchlist', async () => { - // should start unactive - assert.isFalse((await bm.getAccountInfo(watchAddress1)).isActive) - // add first watchlist - let setTx = await bm - .connect(owner) - .setWatchList([watchAddress1], [oneEth], [twoEth]) - await setTx.wait() - let watchList = await bm.getWatchList() - assert.deepEqual(watchList, [watchAddress1]) - const accountInfo = await bm.getAccountInfo(watchAddress1) - assert.isTrue(accountInfo.isActive) - expect(accountInfo.minBalanceWei).to.equal(oneEth) - expect(accountInfo.topUpAmountWei).to.equal(twoEth) - // add more to watchlist - setTx = await bm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2, watchAddress3], - [oneEth, twoEth, threeEth], - [oneEth, twoEth, threeEth], - ) - await setTx.wait() - watchList = await bm.getWatchList() - assert.deepEqual(watchList, [watchAddress1, watchAddress2, watchAddress3]) - let accountInfo1 = await bm.getAccountInfo(watchAddress1) - let accountInfo2 = await bm.getAccountInfo(watchAddress2) - let accountInfo3 = await bm.getAccountInfo(watchAddress3) - expect(accountInfo1.isActive).to.be.true - expect(accountInfo1.minBalanceWei).to.equal(oneEth) - expect(accountInfo1.topUpAmountWei).to.equal(oneEth) - expect(accountInfo2.isActive).to.be.true - expect(accountInfo2.minBalanceWei).to.equal(twoEth) - expect(accountInfo2.topUpAmountWei).to.equal(twoEth) - expect(accountInfo3.isActive).to.be.true - expect(accountInfo3.minBalanceWei).to.equal(threeEth) - expect(accountInfo3.topUpAmountWei).to.equal(threeEth) - // remove some from watchlist - setTx = await bm - .connect(owner) - .setWatchList( - [watchAddress3, watchAddress1], - [threeEth, oneEth], - [threeEth, oneEth], - ) - await setTx.wait() - watchList = await bm.getWatchList() - assert.deepEqual(watchList, [watchAddress3, watchAddress1]) - accountInfo1 = await bm.getAccountInfo(watchAddress1) - accountInfo2 = await bm.getAccountInfo(watchAddress2) - accountInfo3 = await bm.getAccountInfo(watchAddress3) - expect(accountInfo1.isActive).to.be.true - expect(accountInfo2.isActive).to.be.false - expect(accountInfo3.isActive).to.be.true - }) - - it('Should not allow duplicates in the watchlist', async () => { - const errMsg = `DuplicateAddress` - const setTx = bm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2, watchAddress1], - [oneEth, twoEth, threeEth], - [oneEth, twoEth, threeEth], - ) - await expect(setTx) - .to.be.revertedWithCustomError(bm, errMsg) - .withArgs(watchAddress1) - }) - - it('Should not allow strangers to set the watchlist', async () => { - const setTxStranger = bm - .connect(stranger) - .setWatchList([watchAddress1], [oneEth], [twoEth]) - await expect(setTxStranger).to.be.revertedWith(OWNABLE_ERR) - }) - - it('Should revert if the list lengths differ', async () => { - let tx = bm.connect(owner).setWatchList([watchAddress1], [], [twoEth]) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - tx = bm.connect(owner).setWatchList([watchAddress1], [oneEth], []) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - tx = bm.connect(owner).setWatchList([], [oneEth], [twoEth]) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - }) - - it('Should revert if any of the addresses are empty', async () => { - let tx = bm - .connect(owner) - .setWatchList( - [watchAddress1, ethers.constants.AddressZero], - [oneEth, oneEth], - [twoEth, twoEth], - ) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - }) - - it('Should revert if any of the top up amounts are 0', async () => { - const tx = bm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2], - [oneEth, oneEth], - [twoEth, zeroEth], - ) - await expect(tx).to.be.revertedWithCustomError(bm, INVALID_WATCHLIST_ERR) - }) - }) - - describe('getKeeperRegistryAddress() / setKeeperRegistryAddress()', () => { - const newAddress = ethers.Wallet.createRandom().address - - it('Should initialize with the registry address provided to the constructor', async () => { - const address = await bm.getKeeperRegistryAddress() - assert.equal(address, keeperRegistry.address) - }) - - it('Should allow the owner to set the registry address', async () => { - const setTx = await bm.connect(owner).setKeeperRegistryAddress(newAddress) - await setTx.wait() - const address = await bm.getKeeperRegistryAddress() - assert.equal(address, newAddress) - }) - - it('Should not allow strangers to set the registry address', async () => { - const setTx = bm.connect(stranger).setKeeperRegistryAddress(newAddress) - await expect(setTx).to.be.revertedWith(OWNABLE_ERR) - }) - - it('Should emit an event', async () => { - const setTx = await bm.connect(owner).setKeeperRegistryAddress(newAddress) - await expect(setTx) - .to.emit(bm, 'KeeperRegistryAddressUpdated') - .withArgs(keeperRegistry.address, newAddress) - }) - }) - - describe('getMinWaitPeriodSeconds / setMinWaitPeriodSeconds()', () => { - const newWaitPeriod = BigNumber.from(1) - - it('Should initialize with the wait period provided to the constructor', async () => { - const minWaitPeriod = await bm.getMinWaitPeriodSeconds() - expect(minWaitPeriod).to.equal(0) - }) - - it('Should allow owner to set the wait period', async () => { - const setTx = await bm - .connect(owner) - .setMinWaitPeriodSeconds(newWaitPeriod) - await setTx.wait() - const minWaitPeriod = await bm.getMinWaitPeriodSeconds() - expect(minWaitPeriod).to.equal(newWaitPeriod) - }) - - it('Should not allow strangers to set the wait period', async () => { - const setTx = bm.connect(stranger).setMinWaitPeriodSeconds(newWaitPeriod) - await expect(setTx).to.be.revertedWith(OWNABLE_ERR) - }) - - it('Should emit an event', async () => { - const setTx = await bm - .connect(owner) - .setMinWaitPeriodSeconds(newWaitPeriod) - await expect(setTx) - .to.emit(bm, 'MinWaitPeriodUpdated') - .withArgs(0, newWaitPeriod) - }) - }) - - describe('checkUpkeep() / getUnderfundedAddresses()', () => { - beforeEach(async () => { - const setTx = await bm.connect(owner).setWatchList( - [ - watchAddress1, // needs funds - watchAddress5, // funded - watchAddress2, // needs funds - watchAddress6, // funded - watchAddress3, // needs funds - ], - new Array(5).fill(oneEth), - new Array(5).fill(twoEth), - ) - await setTx.wait() - }) - - it('Should return list of address that are underfunded', async () => { - const fundTx = await owner.sendTransaction({ - to: bm.address, - value: sixEth, // needs 6 total - }) - await fundTx.wait() - const [should, payload] = await bm.checkUpkeep('0x') - assert.isTrue(should) - let [addresses] = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - payload, - ) - assert.deepEqual(addresses, [watchAddress1, watchAddress2, watchAddress3]) - // checkUpkeep payload should match getUnderfundedAddresses() - addresses = await bm.getUnderfundedAddresses() - assert.deepEqual(addresses, [watchAddress1, watchAddress2, watchAddress3]) - }) - - it('Should return some results even if contract cannot fund all eligible targets', async () => { - const fundTx = await owner.sendTransaction({ - to: bm.address, - value: fiveEth, // needs 6 total - }) - await fundTx.wait() - const [should, payload] = await bm.checkUpkeep('0x') - assert.isTrue(should) - const [addresses] = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - payload, - ) - assert.deepEqual(addresses, [watchAddress1, watchAddress2]) - }) - - it('Should omit addresses that have been funded recently', async () => { - const setWaitPdTx = await bm.setMinWaitPeriodSeconds(3600) // 1 hour - const fundTx = await owner.sendTransaction({ - to: bm.address, - value: sixEth, - }) - await Promise.all([setWaitPdTx.wait(), fundTx.wait()]) - const block = await ethers.provider.getBlock('latest') - const setTopUpTx = await bm.setLastTopUpXXXTestOnly( - watchAddress2, - block.timestamp - 100, - ) - await setTopUpTx.wait() - const [should, payload] = await bm.checkUpkeep('0x') - assert.isTrue(should) - const [addresses] = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - payload, - ) - assert.deepEqual(addresses, [watchAddress1, watchAddress3]) - }) - - it('Should revert when paused', async () => { - const tx = await bm.connect(owner).pause() - await tx.wait() - const ethCall = bm.checkUpkeep('0x') - await expect(ethCall).to.be.revertedWith(PAUSED_ERR) - }) - }) - - describe('performUpkeep()', () => { - let validPayload: string - let invalidPayload: string - - beforeEach(async () => { - validPayload = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [[watchAddress1, watchAddress2, watchAddress3]], - ) - invalidPayload = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [[watchAddress1, watchAddress2, watchAddress4, watchAddress5]], - ) - const setTx = await bm.connect(owner).setWatchList( - [ - watchAddress1, // needs funds - watchAddress5, // funded - watchAddress2, // needs funds - watchAddress6, // funded - watchAddress3, // needs funds - // watchAddress4 - omitted - ], - new Array(5).fill(oneEth), - new Array(5).fill(twoEth), - ) - await setTx.wait() - }) - - it('Should revert when paused', async () => { - const pauseTx = await bm.connect(owner).pause() - await pauseTx.wait() - const performTx = bm.connect(keeperRegistry).performUpkeep(validPayload) - await expect(performTx).to.be.revertedWith(PAUSED_ERR) - }) - - context('when partially funded', () => { - it('Should fund as many addresses as possible', async () => { - const fundTx = await owner.sendTransaction({ - to: bm.address, - value: fiveEth, // only enough eth to fund 2 addresses - }) - await fundTx.wait() - await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(validPayload) - await assertWatchlistBalances(2, 2, 0, 0, 10_000, 10_000) - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(watchAddress1) - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(watchAddress2) - }) - }) - - context('when fully funded', () => { - beforeEach(async () => { - const fundTx = await owner.sendTransaction({ - to: bm.address, - value: tenEth, - }) - await fundTx.wait() - }) - - it('Should fund the appropriate addresses', async () => { - await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(validPayload, { gasLimit: 2_500_000 }) - await performTx.wait() - await assertWatchlistBalances(2, 2, 2, 0, 10_000, 10_000) - }) - - it('Should only fund active, underfunded addresses', async () => { - await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(invalidPayload, { gasLimit: 2_500_000 }) - await performTx.wait() - await assertWatchlistBalances(2, 2, 0, 0, 10_000, 10_000) - }) - - it('Should continue funding addresses even if one reverts', async () => { - await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000) - const addresses = [ - watchAddress1, - receiveReverter.address, - watchAddress2, - ] - const setTx = await bm - .connect(owner) - .setWatchList( - addresses, - new Array(3).fill(oneEth), - new Array(3).fill(twoEth), - ) - await setTx.wait() - const payload = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [addresses], - ) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(payload, { gasLimit: 2_500_000 }) - await performTx.wait() - await assertWatchlistBalances(2, 2, 0, 0, 10_000, 10_000) - await h.assertBalance(receiveReverter.address, 0) - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(watchAddress1) - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(watchAddress2) - await expect(performTx) - .to.emit(bm, 'TopUpFailed') - .withArgs(receiveReverter.address) - }) - - it('Should not fund addresses that have been funded recently', async () => { - const setWaitPdTx = await bm.setMinWaitPeriodSeconds(3600) // 1 hour - await setWaitPdTx.wait() - const block = await ethers.provider.getBlock('latest') - const setTopUpTx = await bm.setLastTopUpXXXTestOnly( - watchAddress2, - block.timestamp - 100, - ) - await setTopUpTx.wait() - await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(validPayload, { gasLimit: 2_500_000 }) - await performTx.wait() - await assertWatchlistBalances(2, 0, 2, 0, 10_000, 10_000) - }) - - it('Should only be callable by the keeper registry contract', async () => { - let performTx = bm.connect(owner).performUpkeep(validPayload) - await expect(performTx).to.be.revertedWithCustomError( - bm, - ONLY_KEEPER_ERR, - ) - performTx = bm.connect(stranger).performUpkeep(validPayload) - await expect(performTx).to.be.revertedWithCustomError( - bm, - ONLY_KEEPER_ERR, - ) - }) - - it('Should protect against running out of gas', async () => { - await assertWatchlistBalances(0, 0, 0, 0, 10_000, 10_000) - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(validPayload, { gasLimit: 130_000 }) // too little for all 3 transfers - await performTx.wait() - const balance1 = await ethers.provider.getBalance(watchAddress1) - const balance2 = await ethers.provider.getBalance(watchAddress2) - const balance3 = await ethers.provider.getBalance(watchAddress3) - const balances = [balance1, balance2, balance3].map((n) => n.toString()) - expect(balances) - .to.include(twoEth.toString()) // expect at least 1 transfer - .to.include(zeroEth.toString()) // expect at least 1 out of funds - }) - - it('Should provide enough gas to support receive and fallback functions', async () => { - const addresses = [ - receiveEmitter.address, - receiveFallbackEmitter.address, - ] - const payload = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [addresses], - ) - const setTx = await bm - .connect(owner) - .setWatchList( - addresses, - new Array(2).fill(oneEth), - new Array(2).fill(twoEth), - ) - await setTx.wait() - - const reBalanceBefore = await ethers.provider.getBalance( - receiveEmitter.address, - ) - const rfeBalanceBefore = await ethers.provider.getBalance( - receiveFallbackEmitter.address, - ) - - const performTx = await bm - .connect(keeperRegistry) - .performUpkeep(payload, { gasLimit: 2_500_000 }) - await h.assertBalance( - receiveEmitter.address, - reBalanceBefore.add(twoEth), - ) - await h.assertBalance( - receiveFallbackEmitter.address, - rfeBalanceBefore.add(twoEth), - ) - - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(receiveEmitter.address) - await expect(performTx) - .to.emit(bm, 'TopUpSucceeded') - .withArgs(receiveFallbackEmitter.address) - }) - }) - }) - - describe('topUp()', () => { - context('when not paused', () => { - it('Should be callable by anyone', async () => { - const users = [owner, keeperRegistry, stranger] - for (let idx = 0; idx < users.length; idx++) { - const user = users[idx] - await bm.connect(user).topUp([]) - } - }) - }) - context('when paused', () => { - it('Should be callable by no one', async () => { - await bm.connect(owner).pause() - const users = [owner, keeperRegistry, stranger] - for (let idx = 0; idx < users.length; idx++) { - const user = users[idx] - const tx = bm.connect(user).topUp([]) - await expect(tx).to.be.revertedWith(PAUSED_ERR) - } - }) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/IAutomationRegistryMaster2_2.test.ts b/contracts/test/v0.8/automation/IAutomationRegistryMaster2_2.test.ts deleted file mode 100644 index 11da7273ab9..00000000000 --- a/contracts/test/v0.8/automation/IAutomationRegistryMaster2_2.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import fs from 'fs' -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { AutomationRegistry2_2__factory as AutomationRegistryFactory } from '../../../typechain/factories/AutomationRegistry2_2__factory' -import { AutomationRegistryLogicA2_2__factory as AutomationRegistryLogicAFactory } from '../../../typechain/factories/AutomationRegistryLogicA2_2__factory' -import { AutomationRegistryLogicB2_2__factory as AutomationRegistryLogicBFactory } from '../../../typechain/factories/AutomationRegistryLogicB2_2__factory' -import { AutomationRegistryBase2_2__factory as AutomationRegistryBaseFactory } from '../../../typechain/factories/AutomationRegistryBase2_2__factory' -import { Chainable__factory as ChainableFactory } from '../../../typechain/factories/Chainable__factory' -import { IAutomationRegistryMaster__factory as IAutomationRegistryMasterFactory } from '../../../typechain/factories/IAutomationRegistryMaster__factory' -import { IAutomationRegistryConsumer__factory as IAutomationRegistryConsumerFactory } from '../../../typechain/factories/IAutomationRegistryConsumer__factory' -import { MigratableKeeperRegistryInterface__factory as MigratableKeeperRegistryInterfaceFactory } from '../../../typechain/factories/MigratableKeeperRegistryInterface__factory' -import { MigratableKeeperRegistryInterfaceV2__factory as MigratableKeeperRegistryInterfaceV2Factory } from '../../../typechain/factories/MigratableKeeperRegistryInterfaceV2__factory' -import { OCR2Abstract__factory as OCR2AbstractFactory } from '../../../typechain/factories/OCR2Abstract__factory' -import { IAutomationV21PlusCommon__factory as IAutomationV21PlusCommonFactory } from '../../../typechain/factories/IAutomationV21PlusCommon__factory' -import { - assertSatisfiesEvents, - assertSatisfiesInterface, - entryID, -} from './helpers' - -const compositeABIs = [ - AutomationRegistryFactory.abi, - AutomationRegistryLogicAFactory.abi, - AutomationRegistryLogicBFactory.abi, -] - -/** - * @dev because the keeper master interface is a composite of several different contracts, - * it is possible that an interface could be satisfied by functions across different - * contracts, and therefore not enforceable by the compiler directly. Instead, we use this - * test to assert that the master interface satisfies the constraints of an individual interface - */ -describe('IAutomationRegistryMaster2_2', () => { - it('is up to date', async () => { - const checksum = ethers.utils.id(compositeABIs.join('')) - const knownChecksum = fs - .readFileSync( - 'src/v0.8/automation/interfaces/v2_2/IAutomationRegistryMaster.sol', - ) - .toString() - .slice(17, 83) // checksum located at top of file - assert.equal( - checksum, - knownChecksum, - 'master interface is out of date - regenerate using "pnpm ts-node ./scripts/generate-automation-master-interface.ts"', - ) - }) - - it('is generated from composite contracts without competing definitions', async () => { - const sharedEntries = [ - ...ChainableFactory.abi, - ...AutomationRegistryBaseFactory.abi, - ] - const abiSet = new Set() - const sharedSet = new Set() - for (const entry of sharedEntries) { - sharedSet.add(entryID(entry)) - } - for (const abi of compositeABIs) { - for (const entry of abi) { - const id = entryID(entry) - if (!abiSet.has(id)) { - abiSet.add(id) - } else if (!sharedSet.has(id)) { - assert.fail( - `composite contracts contain duplicate entry: ${JSON.stringify( - entry, - )}`, - ) - } - } - } - }) - - it('satisfies the IAutomationRegistryConsumer interface', async () => { - assertSatisfiesInterface( - IAutomationRegistryMasterFactory.abi, - IAutomationRegistryConsumerFactory.abi, - ) - }) - - it('satisfies the MigratableKeeperRegistryInterface interface', async () => { - assertSatisfiesInterface( - IAutomationRegistryMasterFactory.abi, - MigratableKeeperRegistryInterfaceFactory.abi, - ) - }) - - it('satisfies the MigratableKeeperRegistryInterfaceV2 interface', async () => { - assertSatisfiesInterface( - IAutomationRegistryMasterFactory.abi, - MigratableKeeperRegistryInterfaceV2Factory.abi, - ) - }) - - // temporarily disable this test due to this update: https://github.com/smartcontractkit/chainlink/pull/14369/files#diff-6e79d46ea0ef204dea679ffd2a9f4dfccd090d8f405ba2d9bffad527d7b862c6L44 - it.skip('satisfies the OCR2Abstract interface', async () => { - assertSatisfiesInterface( - IAutomationRegistryMasterFactory.abi, - OCR2AbstractFactory.abi, - ) - }) - - it('satisfies the IAutomationV2Common interface', async () => { - assertSatisfiesInterface( - IAutomationRegistryMasterFactory.abi, - IAutomationV21PlusCommonFactory.abi, - ) - }) - - it('satisfies the IAutomationV2Common events', async () => { - assertSatisfiesEvents( - IAutomationRegistryMasterFactory.abi, - IAutomationV21PlusCommonFactory.abi, - ) - }) -}) diff --git a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts b/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts deleted file mode 100644 index f63de3498b1..00000000000 --- a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts +++ /dev/null @@ -1,1077 +0,0 @@ -import { ethers } from 'hardhat' -import chai, { assert, expect } from 'chai' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { loadFixture } from '@nomicfoundation/hardhat-network-helpers' -import * as h from '../../test-helpers/helpers' -import { mineBlock } from '../../test-helpers/helpers' -import { IAggregatorProxy__factory as IAggregatorProxyFactory } from '../../../typechain/factories/IAggregatorProxy__factory' -import { ILinkAvailable__factory as ILinkAvailableFactory } from '../../../typechain/factories/ILinkAvailable__factory' -import { LinkAvailableBalanceMonitor, LinkToken } from '../../../typechain' -import { BigNumber } from 'ethers' -import deepEqualInAnyOrder from 'deep-equal-in-any-order' -import { - deployMockContract, - MockContract, -} from '@ethereum-waffle/mock-contract' - -chai.use(deepEqualInAnyOrder) - -//////////////////////////////// GAS USAGE LIMITS - CHANGE WITH CAUTION ////////////////////////// -// // -// we try to keep gas usage under this amount (max is 5M) // -const TARGET_PERFORM_GAS_LIMIT = 2_000_000 -// we try to keep gas usage under this amount (max is 5M) the test is not a perfectly accurate // -// measurement of gas usage because it relies on mocks which may do fewer storage reads // -// therefore, we keep a healthy margin to avoid running over the limit! // -const TARGET_CHECK_GAS_LIMIT = 3_500_000 -// // -////////////////////////////////////////////////////////////////////////////////////////////////// -const INVALID_WATCHLIST_ERR = `InvalidWatchList` -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 tenLINK = ethers.utils.parseEther('10') -const oneHundredLINK = ethers.utils.parseEther('100') - -const randAddr = () => ethers.Wallet.createRandom().address - -let labm: LinkAvailableBalanceMonitor -let lt: LinkToken -let owner: SignerWithAddress -let stranger: SignerWithAddress -let keeperRegistry: SignerWithAddress -let proxy1: MockContract -let proxy2: MockContract -let proxy3: MockContract -let proxy4: MockContract // leave this proxy / aggregator unconfigured for topUp() testing -let aggregator1: MockContract -let aggregator2: MockContract -let aggregator3: MockContract -let aggregator4: MockContract // leave this proxy / aggregator unconfigured for topUp() testing - -let directTarget1: MockContract // Contracts which are direct target of balance monitoring without proxy -let directTarget2: MockContract - -let watchListAddresses: string[] -let watchListMinBalances: BigNumber[] -let watchListTopUpAmounts: BigNumber[] -let watchListDstChainSelectors: number[] - -async function assertContractLinkBalances( - balance1: BigNumber, - balance2: BigNumber, - balance3: BigNumber, - balance4: BigNumber, - balance5: BigNumber, -) { - await h.assertLinkTokenBalance(lt, aggregator1.address, balance1, 'address 1') - await h.assertLinkTokenBalance(lt, aggregator2.address, balance2, 'address 2') - await h.assertLinkTokenBalance(lt, aggregator3.address, balance3, 'address 3') - await h.assertLinkTokenBalance( - lt, - directTarget1.address, - balance4, - 'address 4', - ) - await h.assertLinkTokenBalance( - lt, - directTarget2.address, - balance5, - 'address 5', - ) -} - -const setup = async () => { - const accounts = await ethers.getSigners() - owner = accounts[0] - stranger = accounts[1] - keeperRegistry = accounts[2] - - proxy1 = await deployMockContract(owner, IAggregatorProxyFactory.abi) - proxy2 = await deployMockContract(owner, IAggregatorProxyFactory.abi) - proxy3 = await deployMockContract(owner, IAggregatorProxyFactory.abi) - proxy4 = await deployMockContract(owner, IAggregatorProxyFactory.abi) - aggregator1 = await deployMockContract(owner, ILinkAvailableFactory.abi) - aggregator2 = await deployMockContract(owner, ILinkAvailableFactory.abi) - aggregator3 = await deployMockContract(owner, ILinkAvailableFactory.abi) - aggregator4 = await deployMockContract(owner, ILinkAvailableFactory.abi) - directTarget1 = await deployMockContract(owner, ILinkAvailableFactory.abi) - directTarget2 = await deployMockContract(owner, ILinkAvailableFactory.abi) - - await proxy1.deployed() - await proxy2.deployed() - await proxy3.deployed() - await proxy4.deployed() - await aggregator1.deployed() - await aggregator2.deployed() - await aggregator3.deployed() - await aggregator4.deployed() - await directTarget1.deployed() - await directTarget2.deployed() - - watchListAddresses = [ - proxy1.address, - proxy2.address, - proxy3.address, - directTarget1.address, - directTarget2.address, - ] - watchListMinBalances = [oneLINK, oneLINK, oneLINK, twoLINK, twoLINK] - watchListTopUpAmounts = [twoLINK, twoLINK, twoLINK, twoLINK, twoLINK] - watchListDstChainSelectors = [1, 2, 3, 4, 5] - - await proxy1.mock.aggregator.returns(aggregator1.address) - await proxy2.mock.aggregator.returns(aggregator2.address) - await proxy3.mock.aggregator.returns(aggregator3.address) - - await aggregator1.mock.linkAvailableForPayment.returns(0) - await aggregator2.mock.linkAvailableForPayment.returns(0) - await aggregator3.mock.linkAvailableForPayment.returns(0) - - await directTarget1.mock.linkAvailableForPayment.returns(0) - await directTarget2.mock.linkAvailableForPayment.returns(0) - - const labmFactory = await ethers.getContractFactory( - 'LinkAvailableBalanceMonitor', - owner, - ) - const ltFactory = await ethers.getContractFactory( - 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', - owner, - ) - - // 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( - owner.address, - lt.address, - minWaitPeriodSeconds, - maxPerform, - maxCheck, - upkeepInterval, - ) - await labm.deployed() - - for (let i = 1; i <= 4; i++) { - const recipient = await accounts[i].getAddress() - await lt.connect(owner).transfer(recipient, oneHundredLINK) - } - - const setTx = await labm - .connect(owner) - .setWatchList( - watchListAddresses, - watchListMinBalances, - watchListTopUpAmounts, - watchListDstChainSelectors, - ) - await setTx.wait() -} - -describe('LinkAvailableBalanceMonitor', () => { - beforeEach(async () => { - await loadFixture(setup) - }) - - describe('add funds', () => { - it('should allow anyone to add funds', async () => { - await lt.transfer(labm.address, oneLINK) - await lt.connect(stranger).transfer(labm.address, oneLINK) - }) - }) - - describe('setTopUpAmount()', () => { - it('configures the top-up amount', async () => { - await labm - .connect(owner) - .setTopUpAmount(directTarget1.address, BigNumber.from(100)) - const report = await labm.getAccountInfo(directTarget1.address) - assert.equal(report.topUpAmount.toString(), '100') - }) - - it('is only callable by the owner', async () => { - 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, 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 () => { - await expect(labm.connect(owner).setMinBalance(proxy4.address, 100)).to.be - .reverted - }) - - it('is only callable by the owner', async () => { - await expect(labm.connect(stranger).setMinBalance(proxy1.address, 100)).to - .be.reverted - }) - }) - - describe('setMaxPerform()', () => { - it('configures the MaxPerform', async () => { - await labm.connect(owner).setMaxPerform(BigNumber.from(100)) - const report = await labm.getMaxPerform() - assert.equal(report.toString(), '100') - }) - - it('is only callable by the owner', async () => { - await expect(labm.connect(stranger).setMaxPerform(100)).to.be.reverted - }) - }) - - describe('setMaxCheck()', () => { - it('configures the MaxCheck', async () => { - await labm.connect(owner).setMaxCheck(BigNumber.from(100)) - const report = await labm.getMaxCheck() - assert.equal(report.toString(), '100') - }) - - it('is only callable by the owner', async () => { - await expect(labm.connect(stranger).setMaxCheck(100)).to.be.reverted - }) - }) - - describe('setUpkeepInterval()', () => { - it('configures the UpkeepInterval', async () => { - await labm.connect(owner).setUpkeepInterval(BigNumber.from(100)) - const report = await labm.getUpkeepInterval() - assert.equal(report.toString(), '100') - }) - - it('is only callable by the owner', async () => { - await expect(labm.connect(stranger).setUpkeepInterval(100)).to.be.reverted - }) - }) - - describe('withdraw()', () => { - beforeEach(async () => { - const tx = await lt.connect(owner).transfer(labm.address, oneLINK) - await tx.wait() - }) - - it('should allow the owner to withdraw', async () => { - const beforeBalance = await lt.balanceOf(owner.address) - const tx = await labm.connect(owner).withdraw(oneLINK, owner.address) - await tx.wait() - const afterBalance = await lt.balanceOf(owner.address) - assert.isTrue( - afterBalance.gt(beforeBalance), - 'balance did not increase after withdraw', - ) - }) - - it('should emit an event', async () => { - const tx = await labm.connect(owner).withdraw(oneLINK, owner.address) - await expect(tx) - .to.emit(labm, 'FundsWithdrawn') - .withArgs(oneLINK, owner.address) - }) - - it('should allow the owner to withdraw to anyone', async () => { - const beforeBalance = await lt.balanceOf(stranger.address) - const tx = await labm.connect(owner).withdraw(oneLINK, stranger.address) - await tx.wait() - const afterBalance = await lt.balanceOf(stranger.address) - assert.isTrue( - beforeBalance.add(oneLINK).eq(afterBalance), - 'balance did not increase after withdraw', - ) - }) - - it('should not allow strangers to withdraw', async () => { - const tx = labm.connect(stranger).withdraw(oneLINK, owner.address) - await expect(tx).to.be.reverted - }) - }) - - describe('pause() / unpause()', () => { - it('should allow owner to pause / unpause', async () => { - const pauseTx = await labm.connect(owner).pause() - await pauseTx.wait() - const unpauseTx = await labm.connect(owner).unpause() - await unpauseTx.wait() - }) - - it('should not allow strangers to pause / unpause', async () => { - const pauseTxStranger = labm.connect(stranger).pause() - await expect(pauseTxStranger).to.be.reverted - const pauseTxOwner = await labm.connect(owner).pause() - await pauseTxOwner.wait() - const unpauseTxStranger = labm.connect(stranger).unpause() - await expect(unpauseTxStranger).to.be.reverted - }) - }) - - describe('setWatchList() / addToWatchListOrDecommissionOrDecommission() / removeFromWatchlist() / getWatchList()', () => { - const watchAddress1 = randAddr() - const watchAddress2 = randAddr() - const watchAddress3 = randAddr() - - beforeEach(async () => { - // reset watchlist to empty before running these tests - 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 - await labm - .connect(owner) - .setWatchList([watchAddress1], [oneLINK], [oneLINK], [0]) - let watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], watchAddress1) - // add more to watchlist - const tx = await labm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2, watchAddress3], - [oneLINK, oneLINK, oneLINK], - [oneLINK, oneLINK, oneLINK], - [1, 2, 3], - ) - await tx.wait() - watchList = await labm.getWatchList() - assert.deepEqual(watchList, [watchAddress1, watchAddress2, watchAddress3]) - }) - - it('should not allow different length arrays in the watchlist', async () => { - const tx = labm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2, watchAddress1], - [oneLINK, oneLINK], - [oneLINK, oneLINK], - [1, 2], - ) - await expect(tx).to.be.revertedWithCustomError( - labm, - INVALID_WATCHLIST_ERR, - ) - }) - - it('should not allow duplicates in the watchlist', async () => { - const tx = labm - .connect(owner) - .setWatchList( - [watchAddress1, watchAddress2, watchAddress1], - [oneLINK, oneLINK, oneLINK], - [oneLINK, oneLINK, oneLINK], - [1, 2, 3], - ) - await expect(tx) - .to.be.revertedWithCustomError(labm, 'DuplicateAddress') - .withArgs(watchAddress1) - }) - - it('should not allow strangers to set the watchlist', async () => { - const setTxStranger = labm - .connect(stranger) - .setWatchList([watchAddress1], [oneLINK], [oneLINK], [0]) - await expect(setTxStranger).to.be.reverted - }) - - it('should revert if any of the addresses are empty', async () => { - const tx = labm - .connect(owner) - .setWatchList( - [watchAddress1, ethers.constants.AddressZero], - [oneLINK, oneLINK], - [oneLINK, oneLINK], - [1, 2], - ) - await expect(tx).to.be.revertedWithCustomError( - labm, - INVALID_WATCHLIST_ERR, - ) - }) - - it('should allow owner to add multiple addresses with dstChainSelector 0 to the watchlist', async () => { - let tx = await labm - .connect(owner) - .addToWatchListOrDecommission(watchAddress1, 0) - await tx.wait - let watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], watchAddress1) - - tx = await labm - .connect(owner) - .addToWatchListOrDecommission(watchAddress2, 0) - await tx.wait - watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], watchAddress1) - assert.deepEqual(watchList[1], watchAddress2) - - tx = await labm - .connect(owner) - .addToWatchListOrDecommission(watchAddress3, 0) - await tx.wait - watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], watchAddress1) - assert.deepEqual(watchList[1], watchAddress2) - assert.deepEqual(watchList[2], watchAddress3) - }) - - it('should allow owner to add only one address with an unique non-zero dstChainSelector 0 to the watchlist', async () => { - let tx = await labm - .connect(owner) - .addToWatchListOrDecommission(watchAddress1, 1) - await tx.wait - let watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], watchAddress1) - - // 1 is active - let report = await labm.getAccountInfo(watchAddress1) - assert.isTrue(report.isActive) - - tx = await labm - .connect(owner) - .addToWatchListOrDecommission(watchAddress2, 1) - await tx.wait - watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], watchAddress2) - - // 2 is active, 1 should be false - report = await labm.getAccountInfo(watchAddress2) - assert.isTrue(report.isActive) - report = await labm.getAccountInfo(watchAddress1) - assert.isFalse(report.isActive) - - tx = await labm - .connect(owner) - .addToWatchListOrDecommission(watchAddress3, 1) - await tx.wait - watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], watchAddress3) - - // 3 is active, 1 and 2 should be false - report = await labm.getAccountInfo(watchAddress3) - assert.isTrue(report.isActive) - report = await labm.getAccountInfo(watchAddress2) - assert.isFalse(report.isActive) - report = await labm.getAccountInfo(watchAddress1) - assert.isFalse(report.isActive) - }) - - it('should not add address 0 to the watchlist', async () => { - await labm - .connect(owner) - .addToWatchListOrDecommission(ethers.constants.AddressZero, 1) - expect(await labm.getWatchList()).to.not.contain( - ethers.constants.AddressZero, - ) - }) - - it('should not allow stangers to add addresses to the watchlist', async () => { - await expect( - labm.connect(stranger).addToWatchListOrDecommission(watchAddress1, 1), - ).to.be.reverted - }) - - it('should allow owner to remove addresses from the watchlist', async () => { - const tx = await labm - .connect(owner) - .addToWatchListOrDecommission(watchAddress1, 1) - await tx.wait - let watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], watchAddress1) - let report = await labm.getAccountInfo(watchAddress1) - assert.isTrue(report.isActive) - - // remove address - await labm.connect(owner).removeFromWatchList(watchAddress1) - - // address should be false - report = await labm.getAccountInfo(watchAddress1) - assert.isFalse(report.isActive) - - watchList = await labm.getWatchList() - assert.deepEqual(watchList, []) - }) - - it('should allow only one address per dstChainSelector', async () => { - // add address1 - await labm.connect(owner).addToWatchListOrDecommission(watchAddress1, 1) - expect(await labm.getWatchList()).to.contain(watchAddress1) - - // add address2 - await labm.connect(owner).addToWatchListOrDecommission(watchAddress2, 1) - - // only address2 has to be in the watchlist - const watchlist = await labm.getWatchList() - expect(watchlist).to.not.contain(watchAddress1) - expect(watchlist).to.contain(watchAddress2) - }) - - it('should delete the onRamp address on a zero-address with same dstChainSelector', async () => { - // add address1 - await labm.connect(owner).addToWatchListOrDecommission(watchAddress1, 1) - expect(await labm.getWatchList()).to.contain(watchAddress1) - - // simulates an onRampSet(zeroAddress, same dstChainSelector) - await labm - .connect(owner) - .addToWatchListOrDecommission(ethers.constants.AddressZero, 1) - - // address1 should be cleaned - const watchlist = await labm.getWatchList() - expect(watchlist).to.not.contain(watchAddress1) - assert.deepEqual(watchlist, []) - }) - }) - - describe('checkUpkeep() / sampleUnderfundedAddresses() [ @skip-coverage ]', () => { - it('should return list of address that are underfunded', async () => { - const fundTx = await lt - .connect(owner) - .transfer(labm.address, oneHundredLINK) - await fundTx.wait() - - await labm.setWatchList( - watchListAddresses, - watchListMinBalances, - watchListTopUpAmounts, - watchListDstChainSelectors, - ) - - const [should, payload] = await labm.checkUpkeep('0x') - assert.isTrue(should) - let [addresses] = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - payload, - ) - - expect(addresses).to.deep.equalInAnyOrder(watchListAddresses) - addresses = await labm.sampleUnderfundedAddresses() - expect(addresses).to.deep.equalInAnyOrder(watchListAddresses) - }) - - it('should return false because the monitor is underfunded', async () => { - // it needs 10 LINKs to fund all 5 upkeeps, but it only has 8 LINKs - const fundTx = await lt - .connect(owner) - .transfer(labm.address, fourLINK.add(fourLINK)) - await fundTx.wait() - - await labm.setWatchList( - watchListAddresses, - watchListMinBalances, - watchListTopUpAmounts, - watchListDstChainSelectors, - ) - - const [should, _] = await labm.checkUpkeep('0x') - assert.isFalse(should) - }) - - it('should omit aggregators that have sufficient funding', async () => { - const fundTx = await lt.connect(owner).transfer( - labm.address, - oneHundredLINK, // enough for anything that needs funding - ) - await fundTx.wait() - - await labm.setWatchList( - [aggregator2.address, directTarget1.address, directTarget2.address], - [oneLINK, twoLINK, twoLINK], - [oneLINK, oneLINK, oneLINK], - [1, 2, 3], - ) - - // 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([ - aggregator2.address, - directTarget1.address, - directTarget2.address, - ]) - - 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([ - directTarget1.address, - directTarget2.address, - ]) - - await directTarget1.mock.linkAvailableForPayment.returns(tenLINK) - addresses = await labm.sampleUnderfundedAddresses() - expect(addresses).to.deep.equalInAnyOrder([directTarget2.address]) - - await directTarget2.mock.linkAvailableForPayment.returns(tenLINK) - addresses = await labm.sampleUnderfundedAddresses() - expect(addresses).to.deep.equalInAnyOrder([]) - }) - - it('should revert when paused', async () => { - const tx = await labm.connect(owner).pause() - await tx.wait() - const ethCall = labm.checkUpkeep('0x') - await expect(ethCall).to.be.revertedWith(PAUSED_ERR) - }) - - context('with a large set of proxies', async () => { - // in this test, we cheat a little bit and point each proxy to the same aggregator, - // which helps cut down on test time - let MAX_PERFORM: number - let MAX_CHECK: number - let proxyAddresses: string[] - let minBalances: BigNumber[] - let topUpAmount: BigNumber[] - let aggregators: MockContract[] - let dstChainSelectors: number[] - - beforeEach(async () => { - MAX_PERFORM = await labm.getMaxPerform() - MAX_CHECK = await labm.getMaxCheck() - proxyAddresses = [] - minBalances = [] - topUpAmount = [] - aggregators = [] - dstChainSelectors = [] - const numAggregators = MAX_CHECK + 50 - for (let idx = 0; idx < numAggregators; idx++) { - const proxy = await deployMockContract( - owner, - IAggregatorProxyFactory.abi, - ) - const aggregator = await deployMockContract( - owner, - ILinkAvailableFactory.abi, - ) - await proxy.mock.aggregator.returns(aggregator.address) - await aggregator.mock.linkAvailableForPayment.returns(0) - proxyAddresses.push(proxy.address) - minBalances.push(oneLINK) - topUpAmount.push(oneLINK) - aggregators.push(aggregator) - dstChainSelectors.push(0) - } - await labm.setWatchList( - proxyAddresses, - minBalances, - topUpAmount, - dstChainSelectors, - ) - const 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() - expect(addresses.length).to.be.lessThanOrEqual(MAX_PERFORM) - }) - - it('should sample from the list of addresses pseudorandomly', async () => { - const firstAddress: string[] = [] - for (let idx = 0; idx < 10; idx++) { - const addresses = await labm.sampleUnderfundedAddresses() - assert.equal(addresses.length, MAX_PERFORM) - assert.equal( - new Set(addresses).size, - MAX_PERFORM, - 'duplicate address found', - ) - firstAddress.push(addresses[0]) - await mineBlock(ethers.provider) - } - assert( - new Set(firstAddress).size > 1, - 'sample did not shuffle starting index', - ) - }) - - it('can check MAX_CHECK upkeeps within the allotted gas limit', async () => { - for (const aggregator of aggregators) { - // here we make no aggregators eligible for funding, requiring the function to - // traverse the whole list - await aggregator.mock.linkAvailableForPayment.returns(tenLINK) - } - await labm.checkUpkeep('0x', { gasLimit: TARGET_CHECK_GAS_LIMIT }) - }) - }) - }) - - describe('performUpkeep()', () => { - let validPayload: string - - beforeEach(async () => { - validPayload = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [watchListAddresses], - ) - await labm - .connect(owner) - .setWatchList( - watchListAddresses, - watchListMinBalances, - watchListTopUpAmounts, - watchListDstChainSelectors, - ) - }) - - it('should revert when paused', async () => { - await labm.connect(owner).pause() - const performTx = labm.connect(keeperRegistry).performUpkeep(validPayload) - await expect(performTx).to.be.revertedWith(PAUSED_ERR) - }) - - it('should fund the appropriate addresses', async () => { - 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() - - await h.assertLinkTokenBalance(lt, aggregator1.address, zeroLINK) - await h.assertLinkTokenBalance(lt, aggregator2.address, zeroLINK) - await h.assertLinkTokenBalance(lt, aggregator3.address, zeroLINK) - await h.assertLinkTokenBalance(lt, directTarget1.address, zeroLINK) - await h.assertLinkTokenBalance(lt, directTarget2.address, zeroLINK) - - const performTx = await labm - .connect(keeperRegistry) - .performUpkeep(validPayload, { gasLimit: 1_500_000 }) - await performTx.wait() - - await h.assertLinkTokenBalance(lt, aggregator1.address, twoLINK) - await h.assertLinkTokenBalance(lt, aggregator2.address, twoLINK) - await h.assertLinkTokenBalance(lt, aggregator3.address, twoLINK) - await h.assertLinkTokenBalance(lt, directTarget1.address, twoLINK) - await h.assertLinkTokenBalance(lt, directTarget2.address, twoLINK) - }) - - it('can handle MAX_PERFORM proxies within gas limit', async () => { - const MAX_PERFORM = await labm.getMaxPerform() - const proxyAddresses = [] - const minBalances = [] - const topUpAmount = [] - const dstChainSelectors = [] - for (let idx = 0; idx < MAX_PERFORM; idx++) { - const proxy = await deployMockContract( - owner, - IAggregatorProxyFactory.abi, - ) - const aggregator = await deployMockContract( - owner, - ILinkAvailableFactory.abi, - ) - await proxy.mock.aggregator.returns(aggregator.address) - await aggregator.mock.linkAvailableForPayment.returns(0) - proxyAddresses.push(proxy.address) - minBalances.push(oneLINK) - topUpAmount.push(oneLINK) - dstChainSelectors.push(0) - } - await labm.setWatchList( - proxyAddresses, - minBalances, - topUpAmount, - dstChainSelectors, - ) - const watchlist = await labm.getWatchList() - expect(watchlist).to.deep.equalInAnyOrder(proxyAddresses) - assert.equal(watchlist.length, minBalances.length) - - // add funds - const wl = await labm.getWatchList() - const 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) - .performUpkeep(payload, { gasLimit: TARGET_PERFORM_GAS_LIMIT }) - }) - }) - - 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] - for (let idx = 0; idx < users.length; idx++) { - const user = users[idx] - await labm.connect(user).topUp([]) - } - }) - }) - - context('when paused', () => { - it('should be callable by no one', async () => { - await labm.connect(owner).pause() - const users = [owner, keeperRegistry, stranger] - for (let idx = 0; idx < users.length; idx++) { - const user = users[idx] - const tx = labm.connect(user).topUp([]) - await expect(tx).to.be.revertedWith(PAUSED_ERR) - } - }) - }) - - context('when fully funded', () => { - beforeEach(async () => { - await lt.connect(owner).transfer(labm.address, tenLINK) - await assertContractLinkBalances( - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - ) - }) - - it('should fund the appropriate addresses', async () => { - const ai1 = await labm.getAccountInfo(proxy1.address) - assert.equal(0, ai1.lastTopUpTimestamp.toNumber()) - const ai4 = await labm.getAccountInfo(directTarget1.address) - assert.equal(0, ai4.lastTopUpTimestamp.toNumber()) - - const tx = await labm.connect(keeperRegistry).topUp(watchListAddresses) - - 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(aggregator1.address, twoLINK) - assert.equal( - (await lt.balanceOf(aggregator1.address)).toBigInt(), - twoLINK.toBigInt(), - ) - const targetInfo1 = await labm.getAccountInfo(proxy1.address) - assert.notEqual(0, targetInfo1.lastTopUpTimestamp.toNumber()) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(aggregator2.address, twoLINK) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(aggregator3.address, twoLINK) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(directTarget1.address, twoLINK) - assert.equal( - (await lt.balanceOf(directTarget1.address)).toBigInt(), - twoLINK.toBigInt(), - ) - const targetInfo4 = await labm.getAccountInfo(directTarget1.address) - assert.notEqual(0, targetInfo4.lastTopUpTimestamp.toNumber()) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(directTarget2.address, twoLINK) - }) - - it('should only fund the addresses provided', async () => { - await labm - .connect(keeperRegistry) - .topUp([proxy1.address, directTarget1.address]) - - 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 () => { - await labm - .connect(owner) - .setWatchList( - [proxy1.address, directTarget1.address], - [oneLINK, oneLINK], - [oneLINK, oneLINK], - [1, 2], - ) - const tx = await labm - .connect(keeperRegistry) - .topUp([ - proxy1.address, - proxy2.address, - proxy3.address, - directTarget1.address, - directTarget2.address, - ]) - - await h.assertLinkTokenBalance(lt, aggregator1.address, oneLINK) - await h.assertLinkTokenBalance(lt, aggregator2.address, zeroLINK) - await h.assertLinkTokenBalance(lt, aggregator3.address, zeroLINK) - await h.assertLinkTokenBalance(lt, directTarget1.address, oneLINK) - await h.assertLinkTokenBalance(lt, directTarget2.address, zeroLINK) - - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(aggregator1.address, oneLINK) - const targetInfo1 = await labm.getAccountInfo(proxy1.address) - assert.notEqual(0, targetInfo1.lastTopUpTimestamp.toNumber()) - - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(directTarget1.address, oneLINK) - await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy2.address) - await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy3.address) - await expect(tx) - .to.emit(labm, 'TopUpBlocked') - .withArgs(directTarget2.address) - const targetInfo5 = await labm.getAccountInfo(directTarget2.address) - assert.equal(0, targetInfo5.lastTopUpTimestamp.toNumber()) - }) - - 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], - [oneLINK, oneLINK], - [1, 2], - ) - const tx = await labm - .connect(keeperRegistry) - .topUp([proxy1.address, proxy4.address]) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(aggregator1.address, oneLINK) - await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy4.address) - }) - - it('should skip an address if the aggregator is invalid', async () => { - await proxy4.mock.aggregator.returns(aggregator4.address) - await labm - .connect(owner) - .setWatchList( - [proxy1.address, proxy4.address], - [oneLINK, oneLINK], - [oneLINK, oneLINK], - [1, 2], - ) - const tx = await labm - .connect(keeperRegistry) - .topUp([proxy1.address, proxy4.address]) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(aggregator1.address, oneLINK) - await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy4.address) - }) - - it('should skip an address if the aggregator has sufficient funding', async () => { - await proxy4.mock.aggregator.returns(aggregator4.address) - await aggregator4.mock.linkAvailableForPayment.returns(tenLINK) - await labm - .connect(owner) - .setWatchList( - [proxy1.address, proxy4.address], - [oneLINK, oneLINK], - [oneLINK, oneLINK], - [1, 2], - ) - const tx = await labm - .connect(keeperRegistry) - .topUp([proxy1.address, proxy4.address]) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(aggregator1.address, oneLINK) - await expect(tx).to.emit(labm, 'TopUpBlocked').withArgs(proxy4.address) - }) - - it('should skip an address if the direct target has sufficient funding', async () => { - await directTarget1.mock.linkAvailableForPayment.returns(tenLINK) - await labm - .connect(owner) - .setWatchList( - [proxy1.address, directTarget1.address], - [oneLINK, oneLINK], - [oneLINK, oneLINK], - [1, 2], - ) - const tx = await labm - .connect(keeperRegistry) - .topUp([proxy1.address, directTarget1.address]) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(aggregator1.address, oneLINK) - assert.equal( - (await lt.balanceOf(aggregator1.address)).toBigInt(), - oneLINK.toBigInt(), - ) - await expect(tx) - .to.emit(labm, 'TopUpBlocked') - .withArgs(directTarget1.address) - }) - }) - - context('when partially funded', () => { - it('should fund as many addresses as possible', async () => { - await lt.connect(owner).transfer( - labm.address, - 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) - - const tx = await labm.connect(keeperRegistry).topUp(watchListAddresses) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(aggregator3.address, twoLINK) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(directTarget1.address, twoLINK) - assert.equal( - (await lt.balanceOf(aggregator3.address)).toBigInt(), - twoLINK.toBigInt(), - ) - assert.equal( - (await lt.balanceOf(directTarget1.address)).toBigInt(), - twoLINK.toBigInt(), - ) - }) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts b/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts deleted file mode 100644 index 0ee244130ab..00000000000 --- a/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts +++ /dev/null @@ -1,402 +0,0 @@ -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.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', - 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.revertedWithCustomError( - upkeepBalanceMonitor, - '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)) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts b/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts deleted file mode 100644 index 7fd811d8226..00000000000 --- a/contracts/test/v0.8/automation/UpkeepTranscoder3_0.test.ts +++ /dev/null @@ -1,576 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { UpkeepTranscoder30__factory as UpkeepTranscoderFactory } from '../../../typechain/factories/UpkeepTranscoder30__factory' -import { UpkeepTranscoder30 as UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder30' -import { KeeperRegistry2_0__factory as KeeperRegistry2_0Factory } from '../../../typechain/factories/KeeperRegistry2_0__factory' -import { LinkToken__factory as LinkTokenFactory } from '../../../typechain/factories/LinkToken__factory' -import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../../typechain/factories/MockV3Aggregator__factory' -import { UpkeepMock__factory as UpkeepMockFactory } from '../../../typechain/factories/UpkeepMock__factory' -import { evmRevert } from '../../test-helpers/matchers' -import { BigNumber, Signer } from 'ethers' -import { getUsers, Personas } from '../../test-helpers/setup' -import { KeeperRegistryLogic2_0__factory as KeeperRegistryLogic20Factory } from '../../../typechain/factories/KeeperRegistryLogic2_0__factory' -import { KeeperRegistry1_3__factory as KeeperRegistry1_3Factory } from '../../../typechain/factories/KeeperRegistry1_3__factory' -import { KeeperRegistryLogic1_3__factory as KeeperRegistryLogicFactory } from '../../../typechain/factories/KeeperRegistryLogic1_3__factory' -import { toWei } from '../../test-helpers/helpers' -import { LinkToken } from '../../../typechain' - -let upkeepMockFactory: UpkeepMockFactory -let upkeepTranscoderFactory: UpkeepTranscoderFactory -let transcoder: UpkeepTranscoder -let linkTokenFactory: LinkTokenFactory -let mockV3AggregatorFactory: MockV3AggregatorFactory -let keeperRegistryFactory20: KeeperRegistry2_0Factory -let keeperRegistryFactory13: KeeperRegistry1_3Factory -let keeperRegistryLogicFactory20: KeeperRegistryLogic20Factory -let keeperRegistryLogicFactory13: KeeperRegistryLogicFactory -let personas: Personas -let owner: Signer -let upkeepsV1: any[] -let upkeepsV2: any[] -let upkeepsV3: any[] -let admins: string[] -let admin0: Signer -let admin1: Signer -const executeGas = BigNumber.from('100000') -const paymentPremiumPPB = BigNumber.from('250000000') -const flatFeeMicroLink = BigNumber.from(0) -const blockCountPerTurn = BigNumber.from(3) -const randomBytes = '0x1234abcd' -const stalenessSeconds = BigNumber.from(43820) -const gasCeilingMultiplier = BigNumber.from(1) -const checkGasLimit = BigNumber.from(20000000) -const fallbackGasPrice = BigNumber.from(200) -const fallbackLinkPrice = BigNumber.from(200000000) -const maxPerformGas = BigNumber.from(5000000) -const minUpkeepSpend = BigNumber.from(0) -const maxCheckDataSize = BigNumber.from(1000) -const maxPerformDataSize = BigNumber.from(1000) -const mode = BigNumber.from(0) -const linkEth = BigNumber.from(300000000) -const gasWei = BigNumber.from(100) -const registryGasOverhead = BigNumber.from('80000') -const balance = 50000000000000 -const amountSpent = 200000000000000 -const target0 = '0xffffffffffffffffffffffffffffffffffffffff' -const target1 = '0xfffffffffffffffffffffffffffffffffffffffe' -const lastKeeper0 = '0x233a95ccebf3c9f934482c637c08b4015cdd6ddd' -const lastKeeper1 = '0x233a95ccebf3c9f934482c637c08b4015cdd6ddc' -enum UpkeepFormat { - V1, - V2, - V3, - V4, -} -const idx = [123, 124] - -async function getUpkeepID(tx: any) { - const receipt = await tx.wait() - return receipt.events[0].args.id -} - -const encodeConfig = (config: any) => { - return ethers.utils.defaultAbiCoder.encode( - [ - '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)', - ], - [config], - ) -} - -const encodeUpkeepV1 = (ids: number[], upkeeps: any[], checkDatas: any[]) => { - return ethers.utils.defaultAbiCoder.encode( - [ - 'uint256[]', - 'tuple(uint96,address,uint32,uint64,address,uint96,address)[]', - 'bytes[]', - ], - [ids, upkeeps, checkDatas], - ) -} - -const encodeUpkeepV2 = (ids: number[], upkeeps: any[], checkDatas: any[]) => { - return ethers.utils.defaultAbiCoder.encode( - [ - 'uint256[]', - 'tuple(uint96,address,uint96,address,uint32,uint32,address,bool)[]', - 'bytes[]', - ], - [ids, upkeeps, checkDatas], - ) -} - -const encodeUpkeepV3 = ( - ids: number[], - upkeeps: any[], - checkDatas: any[], - admins: string[], -) => { - return ethers.utils.defaultAbiCoder.encode( - [ - 'uint256[]', - 'tuple(uint32,uint32,bool,address,uint96,uint96,uint32)[]', - 'bytes[]', - 'address[]', - ], - [ids, upkeeps, checkDatas, admins], - ) -} - -before(async () => { - // @ts-ignore bug in autogen file - upkeepTranscoderFactory = await ethers.getContractFactory( - 'UpkeepTranscoder3_0', - ) - personas = (await getUsers()).personas - - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', - ) - // need full path because there are two contracts with name MockV3Aggregator - mockV3AggregatorFactory = (await ethers.getContractFactory( - 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator', - )) as unknown as MockV3AggregatorFactory - - upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') - - owner = personas.Norbert - admin0 = personas.Neil - admin1 = personas.Nick - admins = [ - (await admin0.getAddress()).toLowerCase(), - (await admin1.getAddress()).toLowerCase(), - ] -}) - -async function deployLinkToken() { - return await linkTokenFactory.connect(owner).deploy() -} - -async function deployFeeds() { - return [ - await mockV3AggregatorFactory.connect(owner).deploy(0, gasWei), - await mockV3AggregatorFactory.connect(owner).deploy(9, linkEth), - ] -} - -async function deployLegacyRegistry1_2( - linkToken: LinkToken, - gasPriceFeed: any, - linkEthFeed: any, -) { - const mock = await upkeepMockFactory.deploy() - // @ts-ignore bug in autogen file - const keeperRegistryFactory = - await ethers.getContractFactory('KeeperRegistry1_2') - transcoder = await upkeepTranscoderFactory.connect(owner).deploy() - const legacyRegistry = await keeperRegistryFactory - .connect(owner) - .deploy(linkToken.address, linkEthFeed.address, gasPriceFeed.address, { - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrar: ethers.constants.AddressZero, - }) - const tx = await legacyRegistry - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin0.getAddress(), - randomBytes, - ) - const id = await getUpkeepID(tx) - return [id, legacyRegistry] -} - -async function deployLegacyRegistry1_3( - linkToken: LinkToken, - gasPriceFeed: any, - linkEthFeed: any, -) { - const mock = await upkeepMockFactory.deploy() - // @ts-ignore bug in autogen file - keeperRegistryFactory13 = await ethers.getContractFactory('KeeperRegistry1_3') - // @ts-ignore bug in autogen file - keeperRegistryLogicFactory13 = await ethers.getContractFactory( - 'KeeperRegistryLogic1_3', - ) - - const registryLogic13 = await keeperRegistryLogicFactory13 - .connect(owner) - .deploy( - 0, - registryGasOverhead, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - ) - - const config = { - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrar: ethers.constants.AddressZero, - } - const Registry1_3 = await keeperRegistryFactory13 - .connect(owner) - .deploy(registryLogic13.address, config) - - const tx = await Registry1_3.connect(owner).registerUpkeep( - mock.address, - executeGas, - await admin0.getAddress(), - randomBytes, - ) - const id = await getUpkeepID(tx) - - return [id, Registry1_3] -} - -async function deployRegistry2_0( - linkToken: LinkToken, - gasPriceFeed: any, - linkEthFeed: any, -) { - // @ts-ignore bug in autogen file - keeperRegistryFactory20 = await ethers.getContractFactory('KeeperRegistry2_0') - // @ts-ignore bug in autogen file - keeperRegistryLogicFactory20 = await ethers.getContractFactory( - 'KeeperRegistryLogic2_0', - ) - - const config = { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrar: ethers.constants.AddressZero, - } - - const registryLogic = await keeperRegistryLogicFactory20 - .connect(owner) - .deploy(mode, linkToken.address, linkEthFeed.address, gasPriceFeed.address) - - const Registry2_0 = await keeperRegistryFactory20 - .connect(owner) - .deploy(registryLogic.address) - - // deploys a registry, setups of initial configuration, registers an upkeep - const keeper1 = personas.Carol - const keeper2 = personas.Eddy - const keeper3 = personas.Nancy - const keeper4 = personas.Norbert - const keeper5 = personas.Nick - const payee1 = personas.Nelly - const payee2 = personas.Norbert - const payee3 = personas.Nick - const payee4 = personas.Eddy - const payee5 = personas.Carol - // signers - const signer1 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000001', - ) - const signer2 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000002', - ) - const signer3 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000003', - ) - const signer4 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000004', - ) - const signer5 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000005', - ) - - const keeperAddresses = [ - await keeper1.getAddress(), - await keeper2.getAddress(), - await keeper3.getAddress(), - await keeper4.getAddress(), - await keeper5.getAddress(), - ] - const payees = [ - await payee1.getAddress(), - await payee2.getAddress(), - await payee3.getAddress(), - await payee4.getAddress(), - await payee5.getAddress(), - ] - const signers = [signer1, signer2, signer3, signer4, signer5] - - const signerAddresses = [] - for (const signer of signers) { - signerAddresses.push(await signer.getAddress()) - } - - const f = 1 - const offchainVersion = 1 - const offchainBytes = '0x' - - await Registry2_0.connect(owner).setConfig( - signerAddresses, - keeperAddresses, - f, - encodeConfig(config), - offchainVersion, - offchainBytes, - ) - await Registry2_0.connect(owner).setPayees(payees) - return Registry2_0 -} - -describe('UpkeepTranscoder3_0', () => { - beforeEach(async () => { - transcoder = await upkeepTranscoderFactory.connect(owner).deploy() - }) - - describe('#typeAndVersion', () => { - it('uses the correct type and version', async () => { - const typeAndVersion = await transcoder.typeAndVersion() - assert.equal(typeAndVersion, 'UpkeepTranscoder 3.0.0') - }) - }) - - describe('#transcodeUpkeeps', () => { - const encodedData = '0xabcd' - - it('reverts if the from type is not V1 or V2', async () => { - await evmRevert( - transcoder.transcodeUpkeeps( - UpkeepFormat.V3, - UpkeepFormat.V1, - encodedData, - ), - ) - await evmRevert( - transcoder.transcodeUpkeeps( - UpkeepFormat.V4, - UpkeepFormat.V1, - encodedData, - ), - ) - }) - - context('when from and to versions are correct', () => { - upkeepsV3 = [ - [executeGas, 2 ** 32 - 1, false, target0, amountSpent, balance, 0], - [executeGas, 2 ** 32 - 1, false, target1, amountSpent, balance, 0], - ] - - it('transcodes V1 upkeeps to V3 properly, regardless of toVersion value', async () => { - upkeepsV1 = [ - [ - balance, - lastKeeper0, - executeGas, - 2 ** 32, - target0, - amountSpent, - await admin0.getAddress(), - ], - [ - balance, - lastKeeper1, - executeGas, - 2 ** 32, - target1, - amountSpent, - await admin1.getAddress(), - ], - ] - - const data = await transcoder.transcodeUpkeeps( - UpkeepFormat.V1, - UpkeepFormat.V1, - encodeUpkeepV1(idx, upkeepsV1, ['0xabcd', '0xffff']), - ) - assert.equal( - encodeUpkeepV3(idx, upkeepsV3, ['0xabcd', '0xffff'], admins), - data, - ) - }) - - it('transcodes V2 upkeeps to V3 properly, regardless of toVersion value', async () => { - upkeepsV2 = [ - [ - balance, - lastKeeper0, - amountSpent, - await admin0.getAddress(), - executeGas, - 2 ** 32 - 1, - target0, - false, - ], - [ - balance, - lastKeeper1, - amountSpent, - await admin1.getAddress(), - executeGas, - 2 ** 32 - 1, - target1, - false, - ], - ] - - const data = await transcoder.transcodeUpkeeps( - UpkeepFormat.V2, - UpkeepFormat.V2, - encodeUpkeepV2(idx, upkeepsV2, ['0xabcd', '0xffff']), - ) - assert.equal( - encodeUpkeepV3(idx, upkeepsV3, ['0xabcd', '0xffff'], admins), - data, - ) - }) - - it('migrates upkeeps from 1.2 registry to 2.0', async () => { - const linkToken = await deployLinkToken() - const [gasPriceFeed, linkEthFeed] = await deployFeeds() - const [id, legacyRegistry] = await deployLegacyRegistry1_2( - linkToken, - gasPriceFeed, - linkEthFeed, - ) - const Registry2_0 = await deployRegistry2_0( - linkToken, - gasPriceFeed, - linkEthFeed, - ) - - await linkToken - .connect(owner) - .approve(legacyRegistry.address, toWei('1000')) - await legacyRegistry.connect(owner).addFunds(id, toWei('1000')) - - // set outgoing permission to registry 2_0 and incoming permission for registry 1_2 - await legacyRegistry.setPeerRegistryMigrationPermission( - Registry2_0.address, - 1, - ) - await Registry2_0.setPeerRegistryMigrationPermission( - legacyRegistry.address, - 2, - ) - - expect((await legacyRegistry.getUpkeep(id)).balance).to.equal( - toWei('1000'), - ) - expect((await legacyRegistry.getUpkeep(id)).checkData).to.equal( - randomBytes, - ) - expect((await legacyRegistry.getState()).state.numUpkeeps).to.equal(1) - - await legacyRegistry - .connect(admin0) - .migrateUpkeeps([id], Registry2_0.address) - - expect((await legacyRegistry.getState()).state.numUpkeeps).to.equal(0) - expect((await Registry2_0.getState()).state.numUpkeeps).to.equal(1) - expect((await legacyRegistry.getUpkeep(id)).balance).to.equal(0) - expect((await legacyRegistry.getUpkeep(id)).checkData).to.equal('0x') - expect((await Registry2_0.getUpkeep(id)).balance).to.equal( - toWei('1000'), - ) - expect( - (await Registry2_0.getState()).state.expectedLinkBalance, - ).to.equal(toWei('1000')) - expect(await linkToken.balanceOf(Registry2_0.address)).to.equal( - toWei('1000'), - ) - expect((await Registry2_0.getUpkeep(id)).checkData).to.equal( - randomBytes, - ) - }) - - it('migrates upkeeps from 1.3 registry to 2.0', async () => { - const linkToken = await deployLinkToken() - const [gasPriceFeed, linkEthFeed] = await deployFeeds() - const [id, legacyRegistry] = await deployLegacyRegistry1_3( - linkToken, - gasPriceFeed, - linkEthFeed, - ) - const Registry2_0 = await deployRegistry2_0( - linkToken, - gasPriceFeed, - linkEthFeed, - ) - - await linkToken - .connect(owner) - .approve(legacyRegistry.address, toWei('1000')) - await legacyRegistry.connect(owner).addFunds(id, toWei('1000')) - - // set outgoing permission to registry 2_0 and incoming permission for registry 1_3 - await legacyRegistry.setPeerRegistryMigrationPermission( - Registry2_0.address, - 1, - ) - await Registry2_0.setPeerRegistryMigrationPermission( - legacyRegistry.address, - 2, - ) - - expect((await legacyRegistry.getUpkeep(id)).balance).to.equal( - toWei('1000'), - ) - expect((await legacyRegistry.getUpkeep(id)).checkData).to.equal( - randomBytes, - ) - expect((await legacyRegistry.getState()).state.numUpkeeps).to.equal(1) - - await legacyRegistry - .connect(admin0) - .migrateUpkeeps([id], Registry2_0.address) - - expect((await legacyRegistry.getState()).state.numUpkeeps).to.equal(0) - expect((await Registry2_0.getState()).state.numUpkeeps).to.equal(1) - expect((await legacyRegistry.getUpkeep(id)).balance).to.equal(0) - expect((await legacyRegistry.getUpkeep(id)).checkData).to.equal('0x') - expect((await Registry2_0.getUpkeep(id)).balance).to.equal( - toWei('1000'), - ) - expect( - (await Registry2_0.getState()).state.expectedLinkBalance, - ).to.equal(toWei('1000')) - expect(await linkToken.balanceOf(Registry2_0.address)).to.equal( - toWei('1000'), - ) - expect((await Registry2_0.getUpkeep(id)).checkData).to.equal( - randomBytes, - ) - }) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts b/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts deleted file mode 100644 index b49dfb1d5b4..00000000000 --- a/contracts/test/v0.8/automation/UpkeepTranscoder4_0.test.ts +++ /dev/null @@ -1,654 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { UpkeepTranscoder4_0 as UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder4_0' -import { KeeperRegistry2_0__factory as KeeperRegistry2_0Factory } from '../../../typechain/factories/KeeperRegistry2_0__factory' -import { LinkToken__factory as LinkTokenFactory } from '../../../typechain/factories/LinkToken__factory' -import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../../typechain/factories/MockV3Aggregator__factory' -import { evmRevert } from '../../test-helpers/matchers' -import { BigNumber, Signer } from 'ethers' -import { getUsers, Personas } from '../../test-helpers/setup' -import { KeeperRegistryLogic2_0__factory as KeeperRegistryLogic20Factory } from '../../../typechain/factories/KeeperRegistryLogic2_0__factory' -import { KeeperRegistry1_3__factory as KeeperRegistry1_3Factory } from '../../../typechain/factories/KeeperRegistry1_3__factory' -import { KeeperRegistryLogic1_3__factory as KeeperRegistryLogicFactory } from '../../../typechain/factories/KeeperRegistryLogic1_3__factory' -import { UpkeepTranscoder4_0__factory as UpkeepTranscoderFactory } from '../../../typechain/factories/UpkeepTranscoder4_0__factory' -import { toWei } from '../../test-helpers/helpers' -import { loadFixture } from '@nomicfoundation/hardhat-network-helpers' -import { - IKeeperRegistryMaster, - KeeperRegistry1_2, - KeeperRegistry1_3, - KeeperRegistry2_0, - LinkToken, - MockV3Aggregator, - UpkeepMock, -} from '../../../typechain' -import { deployRegistry21 } from './helpers' - -////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// - -/*********************************** TRANSCODER v4.0 IS FROZEN ************************************/ - -// We are leaving the original tests enabled, however as automation v2.1 is still actively being deployed - -describe('UpkeepTranscoder v4.0 - Frozen [ @skip-coverage ]', () => { - it('has not changed', () => { - assert.equal( - ethers.utils.id(UpkeepTranscoderFactory.bytecode), - '0xf22c4701b0088e6e69c389a34a22041a69f00890a89246e3c2a6d38172222dae', - 'UpkeepTranscoder bytecode has changed', - ) - }) -}) - -////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// - -let transcoder: UpkeepTranscoder -let linkTokenFactory: LinkTokenFactory -let keeperRegistryFactory20: KeeperRegistry2_0Factory -let keeperRegistryFactory13: KeeperRegistry1_3Factory -let keeperRegistryLogicFactory20: KeeperRegistryLogic20Factory -let keeperRegistryLogicFactory13: KeeperRegistryLogicFactory -let linkToken: LinkToken -let registry12: KeeperRegistry1_2 -let registry13: KeeperRegistry1_3 -let registry20: KeeperRegistry2_0 -let registry21: IKeeperRegistryMaster -let gasPriceFeed: MockV3Aggregator -let linkEthFeed: MockV3Aggregator -let mock: UpkeepMock -let personas: Personas -let owner: Signer -let upkeepsV12: any[] -let upkeepsV13: any[] -let upkeepsV21: any[] -let admins: string[] -let admin0: Signer -let admin1: Signer -let id12: BigNumber -let id13: BigNumber -let id20: BigNumber -const executeGas = BigNumber.from('100000') -const paymentPremiumPPB = BigNumber.from('250000000') -const flatFeeMicroLink = BigNumber.from(0) -const blockCountPerTurn = BigNumber.from(3) -const randomBytes = '0x1234abcd' -const stalenessSeconds = BigNumber.from(43820) -const gasCeilingMultiplier = BigNumber.from(1) -const checkGasLimit = BigNumber.from(20000000) -const fallbackGasPrice = BigNumber.from(200) -const fallbackLinkPrice = BigNumber.from(200000000) -const maxPerformGas = BigNumber.from(5000000) -const minUpkeepSpend = BigNumber.from(0) -const maxCheckDataSize = BigNumber.from(1000) -const maxPerformDataSize = BigNumber.from(1000) -const mode = BigNumber.from(0) -const linkEth = BigNumber.from(300000000) -const gasWei = BigNumber.from(100) -const registryGasOverhead = BigNumber.from('80000') -const balance = 50000000000000 -const amountSpent = 200000000000000 -const { AddressZero } = ethers.constants -const target0 = '0xffffffffffffffffffffffffffffffffffffffff' -const target1 = '0xfffffffffffffffffffffffffffffffffffffffe' -const lastKeeper0 = '0x233a95ccebf3c9f934482c637c08b4015cdd6ddd' -const lastKeeper1 = '0x233a95ccebf3c9f934482c637c08b4015cdd6ddc' - -const f = 1 -const offchainVersion = 1 -const offchainBytes = '0x' -let keeperAddresses: string[] -let signerAddresses: string[] -let payees: string[] - -enum UpkeepFormat { - V12, - V13, - V20, - V21, - V30, // Does not exist -} -const idx = [123, 124] - -async function getUpkeepID(tx: any): Promise { - const receipt = await tx.wait() - return receipt.events[0].args.id -} - -const encodeConfig20 = (config: any) => { - return ethers.utils.defaultAbiCoder.encode( - [ - '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)', - ], - [config], - ) -} - -const encodeUpkeepV12 = (ids: number[], upkeeps: any[], checkDatas: any[]) => { - return ethers.utils.defaultAbiCoder.encode( - [ - 'uint256[]', - 'tuple(uint96,address,uint32,uint64,address,uint96,address)[]', - 'bytes[]', - ], - [ids, upkeeps, checkDatas], - ) -} - -async function deployRegistry1_2(): Promise<[BigNumber, KeeperRegistry1_2]> { - const keeperRegistryFactory = - await ethers.getContractFactory('KeeperRegistry1_2') - const registry12 = await keeperRegistryFactory - .connect(owner) - .deploy(linkToken.address, linkEthFeed.address, gasPriceFeed.address, { - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrar: ethers.constants.AddressZero, - }) - const tx = await registry12 - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin0.getAddress(), - randomBytes, - ) - const id = await getUpkeepID(tx) - return [id, registry12] -} - -async function deployRegistry1_3(): Promise<[BigNumber, KeeperRegistry1_3]> { - keeperRegistryFactory13 = await ethers.getContractFactory('KeeperRegistry1_3') - keeperRegistryLogicFactory13 = await ethers.getContractFactory( - 'KeeperRegistryLogic1_3', - ) - - const registryLogic13 = await keeperRegistryLogicFactory13 - .connect(owner) - .deploy( - 0, - registryGasOverhead, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - ) - - const config = { - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrar: ethers.constants.AddressZero, - } - const registry13 = await keeperRegistryFactory13 - .connect(owner) - .deploy(registryLogic13.address, config) - - const tx = await registry13 - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin0.getAddress(), - randomBytes, - ) - const id = await getUpkeepID(tx) - - return [id, registry13] -} - -async function deployRegistry2_0(): Promise<[BigNumber, KeeperRegistry2_0]> { - keeperRegistryFactory20 = await ethers.getContractFactory('KeeperRegistry2_0') - keeperRegistryLogicFactory20 = await ethers.getContractFactory( - 'KeeperRegistryLogic2_0', - ) - - const config = { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: transcoder.address, - registrar: ethers.constants.AddressZero, - } - - const registryLogic = await keeperRegistryLogicFactory20 - .connect(owner) - .deploy(mode, linkToken.address, linkEthFeed.address, gasPriceFeed.address) - - const registry20 = await keeperRegistryFactory20 - .connect(owner) - .deploy(registryLogic.address) - - await registry20 - .connect(owner) - .setConfig( - signerAddresses, - keeperAddresses, - f, - encodeConfig20(config), - offchainVersion, - offchainBytes, - ) - await registry20.connect(owner).setPayees(payees) - - const tx = await registry20 - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin0.getAddress(), - randomBytes, - randomBytes, - ) - const id = await getUpkeepID(tx) - - return [id, registry20] -} - -async function deployRegistry2_1() { - const registry = await deployRegistry21( - owner, - mode, - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - ) - - const onchainConfig = { - paymentPremiumPPB, - flatFeeMicroLink, - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - minUpkeepSpend, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize: 1000, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - transcoder: ethers.constants.AddressZero, - registrars: [], - upkeepPrivilegeManager: await owner.getAddress(), - } - - await registry - .connect(owner) - .setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - onchainConfig, - offchainVersion, - offchainBytes, - ) - - return registry -} - -const setup = async () => { - personas = (await getUsers()).personas - owner = personas.Norbert - admin0 = personas.Neil - admin1 = personas.Nick - admins = [ - (await admin0.getAddress()).toLowerCase(), - (await admin1.getAddress()).toLowerCase(), - ] - - const upkeepTranscoderFactory = await ethers.getContractFactory( - 'UpkeepTranscoder4_0', - ) - transcoder = await upkeepTranscoderFactory.connect(owner).deploy() - - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', - ) - linkToken = await linkTokenFactory.connect(owner).deploy() - // need full path because there are two contracts with name MockV3Aggregator - const mockV3AggregatorFactory = (await ethers.getContractFactory( - 'src/v0.8/shared/mocks/MockV3Aggregator.sol:MockV3Aggregator', - )) as unknown as MockV3AggregatorFactory - - gasPriceFeed = await mockV3AggregatorFactory.connect(owner).deploy(0, gasWei) - linkEthFeed = await mockV3AggregatorFactory.connect(owner).deploy(9, linkEth) - - const upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') - mock = await upkeepMockFactory.deploy() - - const keeper1 = personas.Carol - const keeper2 = personas.Eddy - const keeper3 = personas.Nancy - const keeper4 = personas.Norbert - const keeper5 = personas.Nick - const payee1 = personas.Nelly - const payee2 = personas.Norbert - const payee3 = personas.Nick - const payee4 = personas.Eddy - const payee5 = personas.Carol - // signers - const signer1 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000001', - ) - const signer2 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000002', - ) - const signer3 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000003', - ) - const signer4 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000004', - ) - const signer5 = new ethers.Wallet( - '0x7777777000000000000000000000000000000000000000000000000000000005', - ) - - keeperAddresses = [ - await keeper1.getAddress(), - await keeper2.getAddress(), - await keeper3.getAddress(), - await keeper4.getAddress(), - await keeper5.getAddress(), - ] - - payees = [ - await payee1.getAddress(), - await payee2.getAddress(), - await payee3.getAddress(), - await payee4.getAddress(), - await payee5.getAddress(), - ] - const signers = [signer1, signer2, signer3, signer4, signer5] - - signerAddresses = signers.map((signer) => signer.address) - ;[id12, registry12] = await deployRegistry1_2() - ;[id13, registry13] = await deployRegistry1_3() - ;[id20, registry20] = await deployRegistry2_0() - registry21 = await deployRegistry2_1() - - upkeepsV12 = [ - [ - balance, - lastKeeper0, - executeGas, - 2 ** 32, - target0, - amountSpent, - await admin0.getAddress(), - ], - [ - balance, - lastKeeper1, - executeGas, - 2 ** 32, - target1, - amountSpent, - await admin1.getAddress(), - ], - ] - - upkeepsV13 = [ - [ - balance, - lastKeeper0, - amountSpent, - await admin0.getAddress(), - executeGas, - 2 ** 32 - 1, - target0, - false, - ], - [ - balance, - lastKeeper1, - amountSpent, - await admin1.getAddress(), - executeGas, - 2 ** 32 - 1, - target1, - false, - ], - ] - - upkeepsV21 = [ - [ - false, - executeGas, - 2 ** 32 - 1, - AddressZero, // forwarder will always be zero - amountSpent, - balance, - 0, - target0, - ], - [ - false, - executeGas, - 2 ** 32 - 1, - AddressZero, // forwarder will always be zero - amountSpent, - balance, - 0, - target1, - ], - ] -} - -describe('UpkeepTranscoder4_0', () => { - beforeEach(async () => { - await loadFixture(setup) - }) - - describe('#typeAndVersion', () => { - it('uses the correct type and version', async () => { - const typeAndVersion = await transcoder.typeAndVersion() - assert.equal(typeAndVersion, 'UpkeepTranscoder 4.0.0') - }) - }) - - describe('#transcodeUpkeeps', () => { - const encodedData = '0xabcd' - - it('reverts if the from type is not v1.2, v1.3, v2.0, or v2.1', async () => { - await evmRevert( - transcoder.transcodeUpkeeps( - UpkeepFormat.V30, - UpkeepFormat.V12, - encodedData, - ), - ) - }) - - context('when from version is correct', () => { - // note this is a bugfix - the "to" version should be accounted for in - // future versions of the transcoder - it('transcodes to v2.1, regardless of toVersion value', async () => { - const data1 = await transcoder.transcodeUpkeeps( - UpkeepFormat.V12, - UpkeepFormat.V12, - encodeUpkeepV12(idx, upkeepsV12, ['0xabcd', '0xffff']), - ) - const data2 = await transcoder.transcodeUpkeeps( - UpkeepFormat.V12, - UpkeepFormat.V13, - encodeUpkeepV12(idx, upkeepsV12, ['0xabcd', '0xffff']), - ) - const data3 = await transcoder.transcodeUpkeeps( - UpkeepFormat.V12, - 100, - encodeUpkeepV12(idx, upkeepsV12, ['0xabcd', '0xffff']), - ) - assert.equal(data1, data2) - assert.equal(data1, data3) - }) - - it('migrates upkeeps from 1.2 registry to 2.1', async () => { - await linkToken - .connect(owner) - .approve(registry12.address, toWei('1000')) - await registry12.connect(owner).addFunds(id12, toWei('1000')) - - await registry12.setPeerRegistryMigrationPermission( - registry21.address, - 1, - ) - await registry21.setPeerRegistryMigrationPermission( - registry12.address, - 2, - ) - - expect((await registry12.getUpkeep(id12)).balance).to.equal( - toWei('1000'), - ) - expect((await registry12.getUpkeep(id12)).checkData).to.equal( - randomBytes, - ) - expect((await registry12.getState()).state.numUpkeeps).to.equal(1) - - await registry12 - .connect(admin0) - .migrateUpkeeps([id12], registry21.address) - - expect((await registry12.getState()).state.numUpkeeps).to.equal(0) - expect((await registry21.getState()).state.numUpkeeps).to.equal(1) - expect((await registry12.getUpkeep(id12)).balance).to.equal(0) - expect((await registry12.getUpkeep(id12)).checkData).to.equal('0x') - expect((await registry21.getUpkeep(id12)).balance).to.equal( - toWei('1000'), - ) - expect( - (await registry21.getState()).state.expectedLinkBalance, - ).to.equal(toWei('1000')) - expect(await linkToken.balanceOf(registry21.address)).to.equal( - toWei('1000'), - ) - expect((await registry21.getUpkeep(id12)).checkData).to.equal( - randomBytes, - ) - expect((await registry21.getUpkeep(id12)).offchainConfig).to.equal('0x') - expect(await registry21.getUpkeepTriggerConfig(id12)).to.equal('0x') - }) - - it('migrates upkeeps from 1.3 registry to 2.1', async () => { - await linkToken - .connect(owner) - .approve(registry13.address, toWei('1000')) - await registry13.connect(owner).addFunds(id13, toWei('1000')) - - await registry13.setPeerRegistryMigrationPermission( - registry21.address, - 1, - ) - await registry21.setPeerRegistryMigrationPermission( - registry13.address, - 2, - ) - - expect((await registry13.getUpkeep(id13)).balance).to.equal( - toWei('1000'), - ) - expect((await registry13.getUpkeep(id13)).checkData).to.equal( - randomBytes, - ) - expect((await registry13.getState()).state.numUpkeeps).to.equal(1) - - await registry13 - .connect(admin0) - .migrateUpkeeps([id13], registry21.address) - - expect((await registry13.getState()).state.numUpkeeps).to.equal(0) - expect((await registry21.getState()).state.numUpkeeps).to.equal(1) - expect((await registry13.getUpkeep(id13)).balance).to.equal(0) - expect((await registry13.getUpkeep(id13)).checkData).to.equal('0x') - expect((await registry21.getUpkeep(id13)).balance).to.equal( - toWei('1000'), - ) - expect( - (await registry21.getState()).state.expectedLinkBalance, - ).to.equal(toWei('1000')) - expect(await linkToken.balanceOf(registry21.address)).to.equal( - toWei('1000'), - ) - expect((await registry21.getUpkeep(id13)).checkData).to.equal( - randomBytes, - ) - expect((await registry21.getUpkeep(id13)).offchainConfig).to.equal('0x') - expect(await registry21.getUpkeepTriggerConfig(id13)).to.equal('0x') - }) - - it('migrates upkeeps from 2.0 registry to 2.1', async () => { - await linkToken - .connect(owner) - .approve(registry20.address, toWei('1000')) - await registry20.connect(owner).addFunds(id20, toWei('1000')) - - await registry20.setPeerRegistryMigrationPermission( - registry21.address, - 1, - ) - await registry21.setPeerRegistryMigrationPermission( - registry20.address, - 2, - ) - - expect((await registry20.getUpkeep(id20)).balance).to.equal( - toWei('1000'), - ) - expect((await registry20.getUpkeep(id20)).checkData).to.equal( - randomBytes, - ) - expect((await registry20.getState()).state.numUpkeeps).to.equal(1) - - await registry20 - .connect(admin0) - .migrateUpkeeps([id20], registry21.address) - - expect((await registry20.getState()).state.numUpkeeps).to.equal(0) - expect((await registry21.getState()).state.numUpkeeps).to.equal(1) - expect((await registry20.getUpkeep(id20)).balance).to.equal(0) - expect((await registry20.getUpkeep(id20)).checkData).to.equal('0x') - expect((await registry21.getUpkeep(id20)).balance).to.equal( - toWei('1000'), - ) - expect( - (await registry21.getState()).state.expectedLinkBalance, - ).to.equal(toWei('1000')) - expect(await linkToken.balanceOf(registry21.address)).to.equal( - toWei('1000'), - ) - expect((await registry21.getUpkeep(id20)).checkData).to.equal( - randomBytes, - ) - expect(await registry21.getUpkeepTriggerConfig(id20)).to.equal('0x') - }) - }) - }) -}) diff --git a/contracts/test/v0.8/automation/helpers.ts b/contracts/test/v0.8/automation/helpers.ts index 99f2cef9b87..130bdcbfecf 100644 --- a/contracts/test/v0.8/automation/helpers.ts +++ b/contracts/test/v0.8/automation/helpers.ts @@ -1,11 +1,5 @@ import { Signer } from 'ethers' import { ethers } from 'hardhat' -import { KeeperRegistryLogicB2_1__factory as KeeperRegistryLogicBFactory } from '../../../typechain/factories/KeeperRegistryLogicB2_1__factory' -import { IKeeperRegistryMaster as IKeeperRegistry } from '../../../typechain/IKeeperRegistryMaster' -import { IKeeperRegistryMaster__factory as IKeeperRegistryMasterFactory } from '../../../typechain/factories/IKeeperRegistryMaster__factory' -import { AutomationRegistryLogicB2_2__factory as AutomationRegistryLogicBFactory } from '../../../typechain/factories/AutomationRegistryLogicB2_2__factory' -import { IAutomationRegistryMaster as IAutomationRegistry } from '../../../typechain/IAutomationRegistryMaster' -import { IAutomationRegistryMaster__factory as IAutomationRegistryMasterFactory } from '../../../typechain/factories/IAutomationRegistryMaster__factory' import { assert } from 'chai' import { FunctionFragment } from '@ethersproject/abi' import { AutomationRegistryLogicC2_3__factory as AutomationRegistryLogicC2_3Factory } from '../../../typechain/factories/AutomationRegistryLogicC2_3__factory' @@ -13,32 +7,6 @@ import { ZKSyncAutomationRegistryLogicC2_3__factory as ZKSyncAutomationRegistryL import { IAutomationRegistryMaster2_3 as IAutomationRegistry2_3 } from '../../../typechain/IAutomationRegistryMaster2_3' import { IAutomationRegistryMaster2_3__factory as IAutomationRegistryMaster2_3Factory } from '../../../typechain/factories/IAutomationRegistryMaster2_3__factory' -export const deployRegistry21 = async ( - from: Signer, - mode: Parameters[0], - link: Parameters[1], - linkNative: Parameters[2], - fastgas: Parameters[3], -): Promise => { - const logicBFactory = await ethers.getContractFactory( - 'KeeperRegistryLogicB2_1', - ) - const logicAFactory = await ethers.getContractFactory( - 'KeeperRegistryLogicA2_1', - ) - const registryFactory = await ethers.getContractFactory('KeeperRegistry2_1') - const forwarderLogicFactory = await ethers.getContractFactory( - 'AutomationForwarderLogic', - ) - const forwarderLogic = await forwarderLogicFactory.connect(from).deploy() - const logicB = await logicBFactory - .connect(from) - .deploy(mode, link, linkNative, fastgas, forwarderLogic.address) - const logicA = await logicAFactory.connect(from).deploy(logicB.address) - const master = await registryFactory.connect(from).deploy(logicA.address) - return IKeeperRegistryMasterFactory.connect(master.address, from) -} - type InterfaceABI = ConstructorParameters[0] type Entry = { inputs?: any[] @@ -130,42 +98,6 @@ export const assertSatisfiesInterface = ( } } -export const deployRegistry22 = async ( - from: Signer, - link: Parameters[0], - linkNative: Parameters[1], - fastgas: Parameters[2], - allowedReadOnlyAddress: Parameters< - AutomationRegistryLogicBFactory['deploy'] - >[3], -): Promise => { - const logicBFactory = await ethers.getContractFactory( - 'AutomationRegistryLogicB2_2', - ) - const logicAFactory = await ethers.getContractFactory( - 'AutomationRegistryLogicA2_2', - ) - const registryFactory = await ethers.getContractFactory( - 'AutomationRegistry2_2', - ) - const forwarderLogicFactory = await ethers.getContractFactory( - 'AutomationForwarderLogic', - ) - const forwarderLogic = await forwarderLogicFactory.connect(from).deploy() - const logicB = await logicBFactory - .connect(from) - .deploy( - link, - linkNative, - fastgas, - forwarderLogic.address, - allowedReadOnlyAddress, - ) - const logicA = await logicAFactory.connect(from).deploy(logicB.address) - const master = await registryFactory.connect(from).deploy(logicA.address) - return IAutomationRegistryMasterFactory.connect(master.address, from) -} - export const deployRegistry23 = async ( from: Signer, link: Parameters[0], From da03b850e76296ef652dfe3532c7aebefd58bea2 Mon Sep 17 00:00:00 2001 From: Mateusz Sekara Date: Wed, 8 Jan 2025 17:23:37 +0100 Subject: [PATCH 2/7] CCIP Config backported from CCIP repo (#15856) * Moving configs directly from CCIP repo * Moving configs directly from CCIP repo --- ccip/config/evm/Astar_Mainnet.toml | 8 +- ccip/config/evm/Astar_Shibuya.toml | 7 +- ccip/config/evm/Avalanche_ANZ_testnet.toml | 4 +- ccip/config/evm/Avalanche_Fuji.toml | 3 +- ccip/config/evm/Avalanche_Mainnet.toml | 4 +- ccip/config/evm/BOB_Mainnet.toml | 28 +++++++ ccip/config/evm/BOB_Testnet.toml | 28 +++++++ ccip/config/evm/BSC_Mainnet.toml | 7 ++ ccip/config/evm/BSC_Testnet.toml | 3 +- ccip/config/evm/Base_Mainnet.toml | 3 + ccip/config/evm/Base_Sepolia.toml | 3 + ccip/config/evm/Berachain_Testnet.toml | 24 ++++++ ccip/config/evm/Bitlayer_Mainnet.toml | 16 ++++ ccip/config/evm/Bitlayer_Testnet.toml | 16 ++++ ccip/config/evm/Blast_Mainnet.toml | 5 +- ccip/config/evm/Blast_Sepolia.toml | 5 +- ccip/config/evm/Bsquared_Mainnet.toml | 23 ++++++ ccip/config/evm/Bsquared_Testnet.toml | 23 ++++++ ccip/config/evm/Celo_Mainnet.toml | 8 +- ccip/config/evm/Celo_Testnet.toml | 3 + ccip/config/evm/Ethereum_Mainnet.toml | 5 ++ ccip/config/evm/Ethereum_Sepolia.toml | 2 + ccip/config/evm/Fantom_Mainnet.toml | 7 +- ccip/config/evm/Fantom_Testnet.toml | 7 +- ccip/config/evm/Gnosis_Chiado.toml | 5 ++ ccip/config/evm/Gnosis_Mainnet.toml | 5 ++ ccip/config/evm/Harmony_Mainnet.toml | 13 +++ ccip/config/evm/Harmony_Testnet.toml | 13 +++ ccip/config/evm/Hashkey_Mainnet.toml | 16 ++++ ccip/config/evm/Hashkey_Testnet.toml | 16 ++++ ccip/config/evm/Heco_Mainnet.toml | 26 ++++++ ccip/config/evm/Hedera_Mainnet.toml | 35 ++++++++ ccip/config/evm/Hedera_Testnet.toml | 35 ++++++++ ccip/config/evm/Klaytn_Mainnet.toml | 15 ++++ ccip/config/evm/Klaytn_Testnet.toml | 15 ++++ ccip/config/evm/Kroma_Mainnet.toml | 8 +- ccip/config/evm/Kroma_Sepolia.toml | 8 +- ccip/config/evm/L3X_Mainnet.toml | 6 +- ccip/config/evm/L3X_Sepolia.toml | 6 +- ccip/config/evm/Linea_Goerli.toml | 17 ++++ ccip/config/evm/Linea_Mainnet.toml | 7 +- ccip/config/evm/Linea_Sepolia.toml | 5 +- ccip/config/evm/Mantle_Mainnet.toml | 33 ++++++++ ccip/config/evm/Mantle_Sepolia.toml | 31 +++++-- ccip/config/evm/Metis_Mainnet.toml | 15 +++- ccip/config/evm/Metis_Sepolia.toml | 5 +- ccip/config/evm/Mode_Mainnet.toml | 3 + ccip/config/evm/Mode_Sepolia.toml | 3 + ccip/config/evm/Optimism_Mainnet.toml | 3 + ccip/config/evm/Optimism_Sepolia.toml | 3 + ccip/config/evm/Polygon_Amoy.toml | 7 +- ccip/config/evm/Polygon_Mainnet.toml | 3 + ccip/config/evm/Polygon_Mumbai.toml | 31 +++++++ ccip/config/evm/Polygon_Zkevm_Cardona.toml | 13 ++- ccip/config/evm/Polygon_Zkevm_Mainnet.toml | 12 +-- ccip/config/evm/RSK_Mainnet.toml | 13 +++ ccip/config/evm/RSK_Testnet.toml | 10 +++ ccip/config/evm/Ronin_Mainnet.toml | 16 ++++ ccip/config/evm/Ronin_Saigon.toml | 16 ++++ ccip/config/evm/Scroll_Mainnet.toml | 3 + ccip/config/evm/Scroll_Sepolia.toml | 3 + ccip/config/evm/Simulated.toml | 6 +- ccip/config/evm/Soneium_Sepolia.toml | 35 ++++++++ ccip/config/evm/Sonic_Mainnet.toml | 28 +++++++ ccip/config/evm/Sonic_Testnet.toml | 28 +++++++ ccip/config/evm/Unichain_Testnet.toml | 26 ++++++ ccip/config/evm/WeMix_Mainnet.toml | 4 +- ccip/config/evm/WeMix_Testnet.toml | 3 +- ccip/config/evm/Worldchain_Mainnet.toml | 23 ++++++ ccip/config/evm/Worldchain_Testnet.toml | 23 ++++++ ccip/config/evm/XLayer_Mainnet.toml | 2 +- ccip/config/evm/XLayer_Sepolia.toml | 3 + ccip/config/evm/fallback.toml | 95 ++++++++++++++++++++++ ccip/config/evm/zkSync_Mainnet.toml | 2 +- ccip/config/evm/zkSync_Sepolia.toml | 15 ++-- 75 files changed, 955 insertions(+), 54 deletions(-) create mode 100644 ccip/config/evm/BOB_Mainnet.toml create mode 100644 ccip/config/evm/BOB_Testnet.toml create mode 100644 ccip/config/evm/Berachain_Testnet.toml create mode 100644 ccip/config/evm/Bitlayer_Mainnet.toml create mode 100644 ccip/config/evm/Bitlayer_Testnet.toml create mode 100644 ccip/config/evm/Bsquared_Mainnet.toml create mode 100644 ccip/config/evm/Bsquared_Testnet.toml create mode 100644 ccip/config/evm/Harmony_Mainnet.toml create mode 100644 ccip/config/evm/Harmony_Testnet.toml create mode 100644 ccip/config/evm/Hashkey_Mainnet.toml create mode 100644 ccip/config/evm/Hashkey_Testnet.toml create mode 100644 ccip/config/evm/Heco_Mainnet.toml create mode 100644 ccip/config/evm/Hedera_Mainnet.toml create mode 100644 ccip/config/evm/Hedera_Testnet.toml create mode 100644 ccip/config/evm/Klaytn_Mainnet.toml create mode 100644 ccip/config/evm/Klaytn_Testnet.toml create mode 100644 ccip/config/evm/Linea_Goerli.toml create mode 100644 ccip/config/evm/Mantle_Mainnet.toml create mode 100644 ccip/config/evm/Polygon_Mumbai.toml create mode 100644 ccip/config/evm/RSK_Mainnet.toml create mode 100644 ccip/config/evm/RSK_Testnet.toml create mode 100644 ccip/config/evm/Ronin_Mainnet.toml create mode 100644 ccip/config/evm/Ronin_Saigon.toml create mode 100755 ccip/config/evm/Soneium_Sepolia.toml create mode 100644 ccip/config/evm/Sonic_Mainnet.toml create mode 100644 ccip/config/evm/Sonic_Testnet.toml create mode 100644 ccip/config/evm/Unichain_Testnet.toml create mode 100644 ccip/config/evm/Worldchain_Mainnet.toml create mode 100644 ccip/config/evm/Worldchain_Testnet.toml create mode 100644 ccip/config/evm/fallback.toml diff --git a/ccip/config/evm/Astar_Mainnet.toml b/ccip/config/evm/Astar_Mainnet.toml index 87808001eb7..5405a67d563 100644 --- a/ccip/config/evm/Astar_Mainnet.toml +++ b/ccip/config/evm/Astar_Mainnet.toml @@ -1,4 +1,5 @@ ChainID = '592' +ChainType = 'astar' FinalityTagEnabled = true FinalityDepth = 100 LogPollInterval = '6s' @@ -6,4 +7,9 @@ LogPollInterval = '6s' [GasEstimator] EIP1559DynamicFees = false PriceMax = '100000 gwei' -LimitDefault = 8000000 \ No newline at end of file +LimitDefault = 8000000 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Astar_Shibuya.toml b/ccip/config/evm/Astar_Shibuya.toml index 5a5df06f6f0..cfcd7c31c75 100644 --- a/ccip/config/evm/Astar_Shibuya.toml +++ b/ccip/config/evm/Astar_Shibuya.toml @@ -6,4 +6,9 @@ LogPollInterval = '6s' [GasEstimator] EIP1559DynamicFees = false PriceMax = '100000 gwei' -LimitDefault = 8000000 \ No newline at end of file +LimitDefault = 8000000 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false \ No newline at end of file diff --git a/ccip/config/evm/Avalanche_ANZ_testnet.toml b/ccip/config/evm/Avalanche_ANZ_testnet.toml index 1242e1ec06e..936a82d5092 100644 --- a/ccip/config/evm/Avalanche_ANZ_testnet.toml +++ b/ccip/config/evm/Avalanche_ANZ_testnet.toml @@ -19,4 +19,6 @@ PriceMin = '25 gwei' BlockHistorySize = 24 [HeadTracker] -PersistenceEnabled = false +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Avalanche_Fuji.toml b/ccip/config/evm/Avalanche_Fuji.toml index 7df1d26a336..4340b6b861d 100644 --- a/ccip/config/evm/Avalanche_Fuji.toml +++ b/ccip/config/evm/Avalanche_Fuji.toml @@ -17,5 +17,6 @@ PriceDefault = '1 gwei' BlockHistorySize = 24 [HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 FinalityTagBypass = false -PersistenceEnabled = false diff --git a/ccip/config/evm/Avalanche_Mainnet.toml b/ccip/config/evm/Avalanche_Mainnet.toml index 341ae5478b3..ac73a7b98fa 100644 --- a/ccip/config/evm/Avalanche_Mainnet.toml +++ b/ccip/config/evm/Avalanche_Mainnet.toml @@ -18,4 +18,6 @@ PriceDefault = '1 gwei' BlockHistorySize = 24 [HeadTracker] -PersistenceEnabled = false +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/BOB_Mainnet.toml b/ccip/config/evm/BOB_Mainnet.toml new file mode 100644 index 00000000000..70cc2fb8ba4 --- /dev/null +++ b/ccip/config/evm/BOB_Mainnet.toml @@ -0,0 +1,28 @@ +ChainID = '60808' +# OP stack https://docs.gobob.xyz/learn/introduction/stack-overview#rollup-layer +ChainType = 'optimismBedrock' +# FinalityDepth in mainnet showed more than 3k +FinalityDepth = 3150 +# block_time was: 2s, adding 1 second buffer +LogPollInterval = '3s' + +# finality_depth * block_time / 60 secs = ~105 min (finality time) +NoNewFinalizedHeadsThreshold = '110m' + +FinalityTagEnabled = true + +[GasEstimator] +EIP1559DynamicFees = true +Mode = 'FeeHistory' + +[GasEstimator.FeeHistory] +# block_time was: 2s, per recommendation skip 1-2 blocks +CacheTimeout = '4s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/BOB_Testnet.toml b/ccip/config/evm/BOB_Testnet.toml new file mode 100644 index 00000000000..bd8505c4e44 --- /dev/null +++ b/ccip/config/evm/BOB_Testnet.toml @@ -0,0 +1,28 @@ +ChainID = '808813' +# OP stack https://docs.gobob.xyz/learn/introduction/stack-overview#rollup-layer +ChainType = 'optimismBedrock' +# FinalityDepth in mainnet showed more than 3k +FinalityDepth = 3150 +# block_time was: 2s, adding 1 second buffer +LogPollInterval = '3s' + +# finality_depth * block_time / 60 secs = ~105 min (finality time) +NoNewFinalizedHeadsThreshold = '110m' + +FinalityTagEnabled = true + +[GasEstimator] +EIP1559DynamicFees = true +Mode = 'FeeHistory' + +[GasEstimator.FeeHistory] +# block_time was: 2s, per recommendation skip 1-2 blocks +CacheTimeout = '4s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/BSC_Mainnet.toml b/ccip/config/evm/BSC_Mainnet.toml index 10f4c570bef..df140e63973 100644 --- a/ccip/config/evm/BSC_Mainnet.toml +++ b/ccip/config/evm/BSC_Mainnet.toml @@ -13,6 +13,8 @@ NoNewFinalizedHeadsThreshold = '45s' [GasEstimator] PriceDefault = '5 gwei' +# Set to the BSC node's default Eth.Miner.GasPrice config +PriceMin = '3 gwei' # 15s delay since feeds update every minute in volatile situations BumpThreshold = 5 @@ -26,3 +28,8 @@ ObservationGracePeriod = '500ms' [NodePool] SyncThreshold = 10 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/BSC_Testnet.toml b/ccip/config/evm/BSC_Testnet.toml index bb13501f1a2..9c528f816ea 100644 --- a/ccip/config/evm/BSC_Testnet.toml +++ b/ccip/config/evm/BSC_Testnet.toml @@ -22,8 +22,9 @@ BlockHistorySize = 24 [HeadTracker] HistoryDepth = 100 SamplingInterval = '1s' +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 FinalityTagBypass = false -PersistenceEnabled = false [OCR] DatabaseTimeout = '2s' diff --git a/ccip/config/evm/Base_Mainnet.toml b/ccip/config/evm/Base_Mainnet.toml index da38182b194..0f895e1bc6b 100644 --- a/ccip/config/evm/Base_Mainnet.toml +++ b/ccip/config/evm/Base_Mainnet.toml @@ -20,6 +20,9 @@ ResendAfterThreshold = '30s' [HeadTracker] HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] SyncThreshold = 10 diff --git a/ccip/config/evm/Base_Sepolia.toml b/ccip/config/evm/Base_Sepolia.toml index 92f7717b27d..202c544fb4b 100644 --- a/ccip/config/evm/Base_Sepolia.toml +++ b/ccip/config/evm/Base_Sepolia.toml @@ -21,6 +21,9 @@ ResendAfterThreshold = '30s' [HeadTracker] HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] SyncThreshold = 10 diff --git a/ccip/config/evm/Berachain_Testnet.toml b/ccip/config/evm/Berachain_Testnet.toml new file mode 100644 index 00000000000..9fc810e8908 --- /dev/null +++ b/ccip/config/evm/Berachain_Testnet.toml @@ -0,0 +1,24 @@ +ChainID = '80084' +# finality_depth: instant +FinalityDepth = 10 +# block_time: 5s, adding 1 second buffer +LogPollInterval = '6s' + +# finality_depth * block_time / 60 secs = ~0.8 min (finality time) +NoNewFinalizedHeadsThreshold = '5m' + +[GasEstimator] +EIP1559DynamicFees = true +Mode = 'FeeHistory' + +[GasEstimator.FeeHistory] +# block_time was: 5s, per recommendation skip 1-2 blocks +CacheTimeout = '10s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Bitlayer_Mainnet.toml b/ccip/config/evm/Bitlayer_Mainnet.toml new file mode 100644 index 00000000000..f6d669d4f78 --- /dev/null +++ b/ccip/config/evm/Bitlayer_Mainnet.toml @@ -0,0 +1,16 @@ +ChainID = '200901' +FinalityTagEnabled = false +FinalityDepth = 21 # confirmed with Bitlayer team and recommended by docs: https://docs.bitlayer.org/docs/Learn/BitlayerNetwork/AboutFinality/#about-finality-at-stage-bitlayer-pos-bitlayer-mainnet-v1 + +[GasEstimator] +Mode = 'FeeHistory' +EIP1559DynamicFees = false +PriceMax = '1 gwei' # DS&A recommended value +PriceMin = '40 mwei' # During testing, we saw minimum gas prices ~50 mwei +PriceDefault = '1 gwei' # As we set PriceMax to '1 gwei' and PriceDefault must be less than or equal to PriceMax +FeeCapDefault = '1 gwei' # As we set PriceMax to '1 gwei' and FeeCapDefault must be less than or equal to PriceMax + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Bitlayer_Testnet.toml b/ccip/config/evm/Bitlayer_Testnet.toml new file mode 100644 index 00000000000..7107527ce2f --- /dev/null +++ b/ccip/config/evm/Bitlayer_Testnet.toml @@ -0,0 +1,16 @@ +ChainID = '200810' +FinalityTagEnabled = false +FinalityDepth = 21 # confirmed with Bitlayer team and recommended by docs: https://docs.bitlayer.org/docs/Learn/BitlayerNetwork/AboutFinality/#about-finality-at-stage-bitlayer-pos-bitlayer-mainnet-v1 + +[GasEstimator] +Mode='FeeHistory' +EIP1559DynamicFees = false +PriceMax = '1 gwei' # DS&A recommended value +PriceMin = '40 mwei' # During testing, we saw minimum gas prices ~50 mwei +PriceDefault = '1 gwei' # As we set PriceMax to '1 gwei' and PriceDefault must be less than or equal to PriceMax +FeeCapDefault = '1 gwei' # As we set PriceMax to '1 gwei' and FeeCapDefault must be less than or equal to PriceMax + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Blast_Mainnet.toml b/ccip/config/evm/Blast_Mainnet.toml index f8b501723ff..26ecddeec54 100644 --- a/ccip/config/evm/Blast_Mainnet.toml +++ b/ccip/config/evm/Blast_Mainnet.toml @@ -26,9 +26,12 @@ EIP1559FeeCapBufferBlocks = 0 [HeadTracker] HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] # 4 block sync time between nodes to ensure they aren't labelled unreachable too soon PollFailureThreshold = 4 # polls every 4sec to check if there is a block produced, since blockRate is ~3sec -PollInterval = '4s' \ No newline at end of file +PollInterval = '4s' diff --git a/ccip/config/evm/Blast_Sepolia.toml b/ccip/config/evm/Blast_Sepolia.toml index 96dc5c67871..55f2356ad3a 100644 --- a/ccip/config/evm/Blast_Sepolia.toml +++ b/ccip/config/evm/Blast_Sepolia.toml @@ -26,9 +26,12 @@ EIP1559FeeCapBufferBlocks = 0 [HeadTracker] HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] # 4 block sync time between nodes to ensure they aren't labelled unreachable too soon PollFailureThreshold = 4 # polls every 4sec to check if there is a block produced, since blockRate is ~3sec -PollInterval = '4s' \ No newline at end of file +PollInterval = '4s' diff --git a/ccip/config/evm/Bsquared_Mainnet.toml b/ccip/config/evm/Bsquared_Mainnet.toml new file mode 100644 index 00000000000..61b0e5337c7 --- /dev/null +++ b/ccip/config/evm/Bsquared_Mainnet.toml @@ -0,0 +1,23 @@ +ChainID = '223' +# OP stack from questionnaire https://docs.google.com/spreadsheets/d/1l8dx1GzxEnjgwH5x3vB60FUr5iFALzPcs6W_wOAiuDs/edit?gid=625078687#gid=625078687 +ChainType = 'optimismBedrock' +# finality_depth was: ~1900 +FinalityDepth = 2000 +# block_time: ~2s, adding 1 second buffer +LogPollInterval = '3s' + +# finality_depth * block_time / 60 secs = ~66 min (finality time) +NoNewFinalizedHeadsThreshold = '70m' + +FinalityTagEnabled = true + +[GasEstimator] +EIP1559DynamicFees = true +Mode = 'FeeHistory' + +[GasEstimator.FeeHistory] +# block_time was: 2s, per recommendation skip 1-2 blocks +CacheTimeout = '4s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 diff --git a/ccip/config/evm/Bsquared_Testnet.toml b/ccip/config/evm/Bsquared_Testnet.toml new file mode 100644 index 00000000000..b7cfd35fc41 --- /dev/null +++ b/ccip/config/evm/Bsquared_Testnet.toml @@ -0,0 +1,23 @@ +ChainID = '1123' +# OP stack from questionnaire https://docs.google.com/spreadsheets/d/1l8dx1GzxEnjgwH5x3vB60FUr5iFALzPcs6W_wOAiuDs/edit?gid=625078687#gid=625078687 +ChainType = 'optimismBedrock' +# finality_depth was: ~1900 +FinalityDepth = 2000 +# block_time: ~2s, adding 1 second buffer +LogPollInterval = '3s' + +# finality_depth * block_time / 60 secs = ~66 min (finality time) +NoNewFinalizedHeadsThreshold = '70m' + +FinalityTagEnabled = true + +[GasEstimator] +EIP1559DynamicFees = true +Mode = 'FeeHistory' + +[GasEstimator.FeeHistory] +# block_time was: 2s, per recommendation skip 1-2 blocks +CacheTimeout = '4s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 diff --git a/ccip/config/evm/Celo_Mainnet.toml b/ccip/config/evm/Celo_Mainnet.toml index 0ed08986d32..9da7d632d0d 100644 --- a/ccip/config/evm/Celo_Mainnet.toml +++ b/ccip/config/evm/Celo_Mainnet.toml @@ -1,6 +1,10 @@ ChainID = '42220' ChainType = 'celo' +# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress. +# We expect to be able to rely only on FinalityTagEnabled=true in the short future. +# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N FinalityDepth = 10 +FinalityTagEnabled = true LogPollInterval = '5s' MinIncomingConfirmations = 1 NoNewHeadsThreshold = '1m' @@ -18,4 +22,6 @@ BlockHistorySize = 12 [HeadTracker] HistoryDepth = 50 -PersistenceEnabled = false +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Celo_Testnet.toml b/ccip/config/evm/Celo_Testnet.toml index 0e4594150dd..c03d855acf6 100644 --- a/ccip/config/evm/Celo_Testnet.toml +++ b/ccip/config/evm/Celo_Testnet.toml @@ -1,5 +1,8 @@ ChainID = '44787' ChainType = 'celo' +# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress. +# We expect to be able to rely only on FinalityTagEnabled=true in the short future. +# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N FinalityTagEnabled = true FinalityDepth = 2750 # mean finality time of ~37 minutes + 500 block buffer LogPollInterval = '1s' # 1 sec block rate diff --git a/ccip/config/evm/Ethereum_Mainnet.toml b/ccip/config/evm/Ethereum_Mainnet.toml index 0bcaf35c648..ec3a78156ed 100644 --- a/ccip/config/evm/Ethereum_Mainnet.toml +++ b/ccip/config/evm/Ethereum_Mainnet.toml @@ -15,3 +15,8 @@ TransactionPercentile = 50 [OCR2.Automation] GasLimit = 10500000 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Ethereum_Sepolia.toml b/ccip/config/evm/Ethereum_Sepolia.toml index 24a0e68f77a..966f091f891 100644 --- a/ccip/config/evm/Ethereum_Sepolia.toml +++ b/ccip/config/evm/Ethereum_Sepolia.toml @@ -14,4 +14,6 @@ TransactionPercentile = 50 GasLimit = 10500000 [HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 FinalityTagBypass = false diff --git a/ccip/config/evm/Fantom_Mainnet.toml b/ccip/config/evm/Fantom_Mainnet.toml index 7e76d94278d..2af504796e0 100644 --- a/ccip/config/evm/Fantom_Mainnet.toml +++ b/ccip/config/evm/Fantom_Mainnet.toml @@ -9,4 +9,9 @@ RPCBlockQueryDelay = 2 Mode = 'SuggestedPrice' [OCR2.Automation] -GasLimit = 3800000 \ No newline at end of file +GasLimit = 3800000 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Fantom_Testnet.toml b/ccip/config/evm/Fantom_Testnet.toml index 5f24a76c2e7..b361a8d14dd 100644 --- a/ccip/config/evm/Fantom_Testnet.toml +++ b/ccip/config/evm/Fantom_Testnet.toml @@ -9,4 +9,9 @@ RPCBlockQueryDelay = 2 Mode = 'SuggestedPrice' [OCR2.Automation] -GasLimit = 3800000 \ No newline at end of file +GasLimit = 3800000 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Gnosis_Chiado.toml b/ccip/config/evm/Gnosis_Chiado.toml index 379377a2266..320aa087209 100644 --- a/ccip/config/evm/Gnosis_Chiado.toml +++ b/ccip/config/evm/Gnosis_Chiado.toml @@ -8,3 +8,8 @@ NoNewFinalizedHeadsThreshold = '2m' [GasEstimator] EIP1559DynamicFees = true PriceMax = '500 gwei' + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Gnosis_Mainnet.toml b/ccip/config/evm/Gnosis_Mainnet.toml index 628646364f5..ec8ac227f78 100644 --- a/ccip/config/evm/Gnosis_Mainnet.toml +++ b/ccip/config/evm/Gnosis_Mainnet.toml @@ -16,3 +16,8 @@ PriceDefault = '1 gwei' PriceMax = '500 gwei' # 1 Gwei is the minimum accepted by the validators (unless whitelisted) PriceMin = '1 gwei' + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Harmony_Mainnet.toml b/ccip/config/evm/Harmony_Mainnet.toml new file mode 100644 index 00000000000..1cee98e77c7 --- /dev/null +++ b/ccip/config/evm/Harmony_Mainnet.toml @@ -0,0 +1,13 @@ +ChainID = '1666600000' +LinkContractAddress = '0x218532a12a389a4a92fC0C5Fb22901D1c19198aA' +LogPollInterval = '2s' +MinIncomingConfirmations = 1 +NoNewHeadsThreshold = '30s' + +[GasEstimator] +PriceDefault = '5 gwei' + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Harmony_Testnet.toml b/ccip/config/evm/Harmony_Testnet.toml new file mode 100644 index 00000000000..8b7c85b9c28 --- /dev/null +++ b/ccip/config/evm/Harmony_Testnet.toml @@ -0,0 +1,13 @@ +ChainID = '1666700000' +LinkContractAddress = '0x8b12Ac23BFe11cAb03a634C1F117D64a7f2cFD3e' +LogPollInterval = '2s' +MinIncomingConfirmations = 1 +NoNewHeadsThreshold = '30s' + +[GasEstimator] +PriceDefault = '5 gwei' + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Hashkey_Mainnet.toml b/ccip/config/evm/Hashkey_Mainnet.toml new file mode 100644 index 00000000000..69450c96f80 --- /dev/null +++ b/ccip/config/evm/Hashkey_Mainnet.toml @@ -0,0 +1,16 @@ +ChainID = '177' +ChainType = 'optimismBedrock' +FinalityTagEnabled = true + +[GasEstimator] +PriceMax = '1000 gwei' +LimitDefault = 8000000 +FeeCapDefault = '1000 gwei' + +[NodePool] +PollFailureThreshold = 2 +PollInterval = '8s' + +[GasEstimator.DAOracle] +OracleType = 'opstack' +OracleAddress = '0x420000000000000000000000000000000000000F' \ No newline at end of file diff --git a/ccip/config/evm/Hashkey_Testnet.toml b/ccip/config/evm/Hashkey_Testnet.toml new file mode 100644 index 00000000000..c342e503a33 --- /dev/null +++ b/ccip/config/evm/Hashkey_Testnet.toml @@ -0,0 +1,16 @@ +ChainID = '133' +ChainType = 'optimismBedrock' +FinalityTagEnabled = true + +[GasEstimator] +PriceMax = '1000 gwei' +LimitDefault = 8000000 +FeeCapDefault = '1000 gwei' + +[NodePool] +PollFailureThreshold = 2 +PollInterval = '8s' + +[GasEstimator.DAOracle] +OracleType = 'opstack' +OracleAddress = '0x420000000000000000000000000000000000000F' \ No newline at end of file diff --git a/ccip/config/evm/Heco_Mainnet.toml b/ccip/config/evm/Heco_Mainnet.toml new file mode 100644 index 00000000000..a39e405be31 --- /dev/null +++ b/ccip/config/evm/Heco_Mainnet.toml @@ -0,0 +1,26 @@ +# Heco uses BSC's settings. +ChainID = '128' +LinkContractAddress = '0x404460C6A5EdE2D891e8297795264fDe62ADBB75' +LogPollInterval = '3s' +NoNewHeadsThreshold = '30s' +RPCBlockQueryDelay = 2 + +[GasEstimator] +PriceDefault = '5 gwei' +BumpThreshold = 5 + +[GasEstimator.BlockHistory] +BlockHistorySize = 24 + +[OCR] +DatabaseTimeout = '2s' +ContractTransmitterTransmitTimeout = '2s' +ObservationGracePeriod = '500ms' + +[NodePool] +SyncThreshold = 10 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Hedera_Mainnet.toml b/ccip/config/evm/Hedera_Mainnet.toml new file mode 100644 index 00000000000..fdd6528e0a4 --- /dev/null +++ b/ccip/config/evm/Hedera_Mainnet.toml @@ -0,0 +1,35 @@ +ChainID = '295' +ChainType = 'hedera' +# Considering the 3-5 (6 including a buffer) seconds of finality and 2 seconds block production +# We set the depth to 6/2 = 3 blocks, setting to 10 for safety +FinalityDepth = 10 +# Hedera has high TPS, so polling less often +LogPollInterval = '10s' +MinIncomingConfirmations = 1 + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'SuggestedPrice' +# Since Hedera dont have mempool and there's no way for a node to front run or a user to bribe a node to submit the transaction earlier than it's consensus timestamp, +# But they have automated congesting pricing throttling which would mean at high sustained level the gasPrice itself could be increased to prevent malicious behaviour. +# Disabling the Bumpthreshold as TXM now implicity handles the bumping after checking on-chain nonce & re-broadcast for Hedera chain type +BumpThreshold = 0 +BumpMin = '10 gwei' +BumpPercent = 20 + +[Transactions] +# To hit throttling you'd need to maintain 15 m gas /sec over a prolonged period of time. +# Because Hedera's block times are every 2 secs it's less less likely to happen as compared to other chains +# Setting this to little higher even though Hedera has High TPS, We have seen 10-12s to get the trasaction mined & 20-25s incase of failures +# Accounting for Node syncs & avoid re-sending txns before fetching the receipt, setting to 2m +ResendAfterThreshold = '2m' + +[NodePool] +SyncThreshold = 10 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Hedera_Testnet.toml b/ccip/config/evm/Hedera_Testnet.toml new file mode 100644 index 00000000000..7e9ec3fe2c6 --- /dev/null +++ b/ccip/config/evm/Hedera_Testnet.toml @@ -0,0 +1,35 @@ +ChainID = '296' +ChainType = 'hedera' +# Considering the 3-5 (6 including a buffer) seconds of finality and 2 seconds block production +# We set the depth to 6/2 = 3 blocks, setting to 10 for safety +FinalityDepth = 10 +# Hedera has high TPS, so polling less often +LogPollInterval = '10s' +MinIncomingConfirmations = 1 + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'SuggestedPrice' +# Since Hedera dont have mempool and there's no way for a node to front run or a user to bribe a node to submit the transaction earlier than it's consensus timestamp, +# But they have automated congesting pricing throttling which would mean at high sustained level the gasPrice itself could be increased to prevent malicious behaviour. +# Disabling the Bumpthreshold as TXM now implicity handles the bumping after checking on-chain nonce & re-broadcast for Hedera chain type +BumpThreshold = 0 +BumpMin = '10 gwei' +BumpPercent = 20 + +[Transactions] +# To hit throttling you'd need to maintain 15 m gas /sec over a prolonged period of time. +# Because Hedera's block times are every 2 secs it's less less likely to happen as compared to other chains +# Setting this to little higher even though Hedera has High TPS, We have seen 10-12s to get the trasaction mined & 20-25s incase of failures +# Accounting for Node syncs & avoid re-sending txns before fetching the receipt, setting to 2m +ResendAfterThreshold = '2m' + +[NodePool] +SyncThreshold = 10 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Klaytn_Mainnet.toml b/ccip/config/evm/Klaytn_Mainnet.toml new file mode 100644 index 00000000000..ff8b97de970 --- /dev/null +++ b/ccip/config/evm/Klaytn_Mainnet.toml @@ -0,0 +1,15 @@ +ChainID = '8217' +FinalityDepth = 10 +MinIncomingConfirmations = 1 +NoNewHeadsThreshold = '30s' +OCR.ContractConfirmations = 1 + +[GasEstimator] +Mode = 'SuggestedPrice' +PriceDefault = '750 gwei' # gwei = ston +BumpThreshold = 5 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Klaytn_Testnet.toml b/ccip/config/evm/Klaytn_Testnet.toml new file mode 100644 index 00000000000..599b604f086 --- /dev/null +++ b/ccip/config/evm/Klaytn_Testnet.toml @@ -0,0 +1,15 @@ +ChainID = '1001' +FinalityDepth = 10 +MinIncomingConfirmations = 1 +NoNewHeadsThreshold = '30s' +OCR.ContractConfirmations = 1 + +[GasEstimator] +Mode = 'SuggestedPrice' +PriceDefault = '750 gwei' # gwei = ston +BumpThreshold = 5 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Kroma_Mainnet.toml b/ccip/config/evm/Kroma_Mainnet.toml index 3a48aa8ae1b..21bbe7c357c 100644 --- a/ccip/config/evm/Kroma_Mainnet.toml +++ b/ccip/config/evm/Kroma_Mainnet.toml @@ -1,6 +1,9 @@ ChainID = '255' ChainType = 'kroma' # Kroma is based on the Optimism Bedrock architechture -FinalityDepth = 400 +# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress. +# We expect to be able to rely only on FinalityTagEnabled=true in the short future. +# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N +FinalityDepth = 700 FinalityTagEnabled = true LogPollInterval = '2s' NoNewHeadsThreshold = '40s' @@ -19,6 +22,9 @@ ResendAfterThreshold = '30s' [HeadTracker] HistoryDepth = 400 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] SyncThreshold = 10 diff --git a/ccip/config/evm/Kroma_Sepolia.toml b/ccip/config/evm/Kroma_Sepolia.toml index 9609a09e076..120737df47b 100644 --- a/ccip/config/evm/Kroma_Sepolia.toml +++ b/ccip/config/evm/Kroma_Sepolia.toml @@ -1,6 +1,9 @@ ChainID = '2358' ChainType = 'kroma' # Kroma is based on the Optimism Bedrock architechture -FinalityDepth = 400 +# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress. +# We expect to be able to rely only on FinalityTagEnabled=true in the short future. +# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N +FinalityDepth = 700 FinalityTagEnabled = true LogPollInterval = '2s' NoNewHeadsThreshold = '40s' @@ -19,6 +22,9 @@ ResendAfterThreshold = '30s' [HeadTracker] HistoryDepth = 400 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] SyncThreshold = 10 diff --git a/ccip/config/evm/L3X_Mainnet.toml b/ccip/config/evm/L3X_Mainnet.toml index 9dd33c9e15d..5f14e5e8e8c 100644 --- a/ccip/config/evm/L3X_Mainnet.toml +++ b/ccip/config/evm/L3X_Mainnet.toml @@ -17,5 +17,7 @@ PriceDefault = '0.1 gwei' FeeCapDefault = '1000 gwei' BumpThreshold = 5 -[GasEstimator.DAOracle] -OracleType = 'arbitrum' +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/L3X_Sepolia.toml b/ccip/config/evm/L3X_Sepolia.toml index c0f6a60e943..ca21bc13d6e 100644 --- a/ccip/config/evm/L3X_Sepolia.toml +++ b/ccip/config/evm/L3X_Sepolia.toml @@ -17,5 +17,7 @@ PriceDefault = '0.1 gwei' FeeCapDefault = '1000 gwei' BumpThreshold = 5 -[GasEstimator.DAOracle] -OracleType = 'arbitrum' +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Linea_Goerli.toml b/ccip/config/evm/Linea_Goerli.toml new file mode 100644 index 00000000000..2c85f9cbc02 --- /dev/null +++ b/ccip/config/evm/Linea_Goerli.toml @@ -0,0 +1,17 @@ +ChainID = '59140' +# Block time 12s, finality < 3m +FinalityDepth = 15 +# Blocks are only emitted when a transaction happens / no empty blocks +NoNewHeadsThreshold = '0' + +[GasEstimator] +BumpPercent = 40 + +[Transactions] +# increase resend time to align with finality +ResendAfterThreshold = '3m' + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Linea_Mainnet.toml b/ccip/config/evm/Linea_Mainnet.toml index 5a89873acae..6614fef9d4e 100644 --- a/ccip/config/evm/Linea_Mainnet.toml +++ b/ccip/config/evm/Linea_Mainnet.toml @@ -1,6 +1,6 @@ ChainID = '59144' -# Block time 12s, finality < 60m -FinalityDepth = 300 +#3s block time ~ 20m finality based on committee decision +FinalityDepth = 600 # Blocks are only emitted when a transaction happens / no empty blocks NoNewHeadsThreshold = '0' @@ -15,6 +15,9 @@ ResendAfterThreshold = '3m' # set greater than finality depth [HeadTracker] HistoryDepth = 350 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [Transactions.AutoPurge] Enabled = true diff --git a/ccip/config/evm/Linea_Sepolia.toml b/ccip/config/evm/Linea_Sepolia.toml index 8f168ee93a6..2837c7ca601 100644 --- a/ccip/config/evm/Linea_Sepolia.toml +++ b/ccip/config/evm/Linea_Sepolia.toml @@ -1,5 +1,5 @@ ChainID = '59141' -FinalityDepth = 900 +FinalityDepth = 200 NoNewHeadsThreshold = '0' [GasEstimator] @@ -11,6 +11,9 @@ ResendAfterThreshold = '3m' [HeadTracker] HistoryDepth = 1000 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [Transactions.AutoPurge] Enabled = true diff --git a/ccip/config/evm/Mantle_Mainnet.toml b/ccip/config/evm/Mantle_Mainnet.toml new file mode 100644 index 00000000000..23d5168a7e9 --- /dev/null +++ b/ccip/config/evm/Mantle_Mainnet.toml @@ -0,0 +1,33 @@ +ChainID = '5000' +FinalityTagEnabled = true +FinalityDepth = 1200 +ChainType = 'optimismBedrock' +LogPollInterval = '2s' +MinIncomingConfirmations = 1 +NoNewFinalizedHeadsThreshold = '40m0s' + +[HeadTracker] +HistoryDepth = 1250 + +[GasEstimator] +PriceMax = '120 gwei' +# Limit values are high as Mantle's GasPrice is in native token (MNT) instead of ETH. Their proprietary TokenRatio parameter is used to adjust fees +LimitDefault = 80_000_000_000 +LimitMax = 100_000_000_000 +BumpMin = '100 wei' +BumpThreshold = 60 +EIP1559DynamicFees = true +FeeCapDefault = '120 gwei' +# Mantle recommends setting Priority Fee to 0 in their docs linked here: https://docs-v2.mantle.xyz/devs/concepts/tx-fee/eip-1559#application-of-eip-1559-in-mantle-v2-tectonic +TipCapDefault = '0 wei' +TipCapMin = '0 wei' + +[GasEstimator.BlockHistory] +# Default is 24, which leads to bumpy gas prices. In CCIP +# we want to smooth out the gas prices, so we increase the sample size. +BlockHistorySize = 200 +# The formula for FeeCap is (current block base fee * (1.125 ^ EIP1559FeeCapBufferBlocks) + tipcap) +# where tipcap is managed by the block history estimators. In the context of CCIP, +# the gas price is relayed to other changes for quotes so we want accurate/avg not pessimistic values. +# So we set this to zero so FeeCap = baseFee + tipcap. +EIP1559FeeCapBufferBlocks = 0 \ No newline at end of file diff --git a/ccip/config/evm/Mantle_Sepolia.toml b/ccip/config/evm/Mantle_Sepolia.toml index ee994a71826..705f91142f2 100644 --- a/ccip/config/evm/Mantle_Sepolia.toml +++ b/ccip/config/evm/Mantle_Sepolia.toml @@ -1,19 +1,34 @@ ChainID = '5003' +FinalityTagEnabled = true +FinalityDepth = 1200 ChainType = 'optimismBedrock' -FinalityDepth = 500 LogPollInterval = '2s' -NoNewHeadsThreshold = '0' MinIncomingConfirmations = 1 +NoNewFinalizedHeadsThreshold = '60m0s' [HeadTracker] -HistoryDepth = 600 +HistoryDepth = 1250 -[GasEstimator] -Mode = 'L2Suggested' -PriceMax = '200 gwei' -LimitDefault = 100000000 -FeeCapDefault = '200 gwei' +[GasEstimator] +PriceMax = '120 gwei' +# Limit values are high as Mantle's GasPrice is in native token (MNT) instead of ETH. Their proprietary TokenRatio parameter is used to adjust fees +LimitDefault = 80000000000 +LimitMax = 100000000000 +BumpMin = '100 wei' +BumpPercent = 20 +BumpThreshold = 60 +EIP1559DynamicFees = true +FeeCapDefault = '120 gwei' +# Mantle reccomends setting Priority Fee to 0 in their docs linked here: https://docs-v2.mantle.xyz/devs/concepts/tx-fee/eip-1559#application-of-eip-1559-in-mantle-v2-tectonic +TipCapDefault = '0 wei' +TipCapMin = '0 wei' [GasEstimator.BlockHistory] +# Default is 24, which leads to bumpy gas prices. In CCIP +# we want to smooth out the gas prices, so we increase the sample size. BlockHistorySize = 200 +# The formula for FeeCap is (current block base fee * (1.125 ^ EIP1559FeeCapBufferBlocks) + tipcap) +# where tipcap is managed by the block history estimators. In the context of CCIP, +# the gas price is relayed to other changes for quotes so we want accurate/avg not pessimistic values. +# So we set this to zero so FeeCap = baseFee + tipcap. EIP1559FeeCapBufferBlocks = 0 \ No newline at end of file diff --git a/ccip/config/evm/Metis_Mainnet.toml b/ccip/config/evm/Metis_Mainnet.toml index f057400d014..a95945e9f1b 100644 --- a/ccip/config/evm/Metis_Mainnet.toml +++ b/ccip/config/evm/Metis_Mainnet.toml @@ -1,8 +1,14 @@ # Metis is an L2 chain based on Optimism. ChainID = '1088' -ChainType = 'metis' +ChainType = 'optimismBedrock' # Sequencer offers absolute finality -FinalityDepth = 10 +# High variation on finality depth triggered a commitee to investigate +# and set 500 as a secure finality depth. +# https://chainlink-core.slack.com/archives/C0725LNLJLA/p1717118469587219 +FinalityDepth = 500 +# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress. +# We expect to be able to rely only on FinalityTagEnabled=true in the short future. +# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N FinalityTagEnabled = true MinIncomingConfirmations = 1 NoNewHeadsThreshold = '0' @@ -19,3 +25,8 @@ BlockHistorySize = 0 [NodePool] SyncThreshold = 10 + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Metis_Sepolia.toml b/ccip/config/evm/Metis_Sepolia.toml index 4ff4056c75d..65247991d31 100644 --- a/ccip/config/evm/Metis_Sepolia.toml +++ b/ccip/config/evm/Metis_Sepolia.toml @@ -1,6 +1,9 @@ ChainID = '59902' ChainType = 'optimismBedrock' -FinalityDepth = 10 +# FT and FD are both present here because the dev effort rely only on FinalityTagEnabled are still in progress. +# We expect to be able to rely only on FinalityTagEnabled=true in the short future. +# https://chainlink-core.slack.com/archives/C05CS33N08N/p1715102940763339?thread_ts=1715102478.537529&cid=C05CS33N08N +FinalityDepth = 3000 FinalityTagEnabled = true MinIncomingConfirmations = 1 NoNewHeadsThreshold = '0' diff --git a/ccip/config/evm/Mode_Mainnet.toml b/ccip/config/evm/Mode_Mainnet.toml index 69a8e93fecd..b586cdacc78 100644 --- a/ccip/config/evm/Mode_Mainnet.toml +++ b/ccip/config/evm/Mode_Mainnet.toml @@ -24,6 +24,9 @@ EIP1559FeeCapBufferBlocks = 0 [HeadTracker] HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] PollFailureThreshold = 2 diff --git a/ccip/config/evm/Mode_Sepolia.toml b/ccip/config/evm/Mode_Sepolia.toml index f7398869beb..d621010b4ef 100644 --- a/ccip/config/evm/Mode_Sepolia.toml +++ b/ccip/config/evm/Mode_Sepolia.toml @@ -24,6 +24,9 @@ EIP1559FeeCapBufferBlocks = 0 [HeadTracker] HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] PollFailureThreshold = 2 diff --git a/ccip/config/evm/Optimism_Mainnet.toml b/ccip/config/evm/Optimism_Mainnet.toml index b0f56a49d90..e1398775495 100644 --- a/ccip/config/evm/Optimism_Mainnet.toml +++ b/ccip/config/evm/Optimism_Mainnet.toml @@ -21,6 +21,9 @@ ResendAfterThreshold = '30s' [HeadTracker] HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] SyncThreshold = 10 diff --git a/ccip/config/evm/Optimism_Sepolia.toml b/ccip/config/evm/Optimism_Sepolia.toml index 1c71aa5dd83..2590feec51a 100644 --- a/ccip/config/evm/Optimism_Sepolia.toml +++ b/ccip/config/evm/Optimism_Sepolia.toml @@ -20,6 +20,9 @@ ResendAfterThreshold = '30s' [HeadTracker] HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] SyncThreshold = 10 diff --git a/ccip/config/evm/Polygon_Amoy.toml b/ccip/config/evm/Polygon_Amoy.toml index b05b3053a8e..eb75eab271b 100644 --- a/ccip/config/evm/Polygon_Amoy.toml +++ b/ccip/config/evm/Polygon_Amoy.toml @@ -11,10 +11,10 @@ NoNewFinalizedHeadsThreshold = '12m' MaxQueued = 5000 [GasEstimator] -EIP1559DynamicFees = true -PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceDefault = '25 gwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '25 gwei' +EIP1559DynamicFees = true BumpMin = '20 gwei' BumpThreshold = 5 @@ -23,6 +23,9 @@ BlockHistorySize = 24 [HeadTracker] HistoryDepth = 2000 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] SyncThreshold = 10 diff --git a/ccip/config/evm/Polygon_Mainnet.toml b/ccip/config/evm/Polygon_Mainnet.toml index bf605cab3c6..555dbfff815 100644 --- a/ccip/config/evm/Polygon_Mainnet.toml +++ b/ccip/config/evm/Polygon_Mainnet.toml @@ -33,6 +33,9 @@ BlockHistorySize = 24 [HeadTracker] # Polygon suffers from a tremendous number of re-orgs, we need to set this to something very large to be conservative enough HistoryDepth = 2000 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [NodePool] SyncThreshold = 10 diff --git a/ccip/config/evm/Polygon_Mumbai.toml b/ccip/config/evm/Polygon_Mumbai.toml new file mode 100644 index 00000000000..83f275a0643 --- /dev/null +++ b/ccip/config/evm/Polygon_Mumbai.toml @@ -0,0 +1,31 @@ +ChainID = '80001' +FinalityDepth = 500 +FinalityTagEnabled = true +LinkContractAddress = '0x326C977E6efc84E512bB9C30f76E30c160eD06FB' +LogPollInterval = '1s' +MinIncomingConfirmations = 5 +NoNewHeadsThreshold = '30s' +RPCBlockQueryDelay = 10 +RPCDefaultBatchSize = 100 + +[Transactions] +MaxQueued = 5000 + +[GasEstimator] +PriceDefault = '25 gwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +PriceMin = '25 gwei' +BumpMin = '20 gwei' +BumpThreshold = 5 + +[GasEstimator.BlockHistory] +BlockHistorySize = 24 + +[HeadTracker] +HistoryDepth = 2000 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false + +[NodePool] +SyncThreshold = 10 diff --git a/ccip/config/evm/Polygon_Zkevm_Cardona.toml b/ccip/config/evm/Polygon_Zkevm_Cardona.toml index 5e4861f9d44..146c23a8024 100644 --- a/ccip/config/evm/Polygon_Zkevm_Cardona.toml +++ b/ccip/config/evm/Polygon_Zkevm_Cardona.toml @@ -13,15 +13,20 @@ ContractConfirmations = 1 ResendAfterThreshold = '3m' [GasEstimator] -PriceMin = '1 mwei' +Mode = 'FeeHistory' +# The FeeHistory estimator does not enforce PriceMin, setting it to 0 to not place any limits on the price +PriceMin = '0' BumpPercent = 40 -BumpMin = '20 mwei' -[GasEstimator.BlockHistory] -BlockHistorySize = 12 +[GasEstimator.FeeHistory] +# Refresh the suggested price every 4 seconds, to stay slightly below their polling rate of 5s +CacheTimeout = '4s' [HeadTracker] HistoryDepth = 2000 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [Transactions.AutoPurge] Enabled = true diff --git a/ccip/config/evm/Polygon_Zkevm_Mainnet.toml b/ccip/config/evm/Polygon_Zkevm_Mainnet.toml index b38a483ff35..d42ef9b057e 100644 --- a/ccip/config/evm/Polygon_Zkevm_Mainnet.toml +++ b/ccip/config/evm/Polygon_Zkevm_Mainnet.toml @@ -1,6 +1,6 @@ ChainID = '1101' ChainType = 'zkevm' -FinalityDepth = 500 +FinalityDepth = 1000 NoNewHeadsThreshold = '6m' MinIncomingConfirmations = 1 LogPollInterval = '30s' @@ -14,12 +14,14 @@ ContractConfirmations = 1 ResendAfterThreshold = '3m' [GasEstimator] -PriceMin = '100 mwei' +Mode = 'FeeHistory' +# The FeeHistory estimator does not enforce PriceMin, setting it to 0 to not place any limits on the price +PriceMin = '0' BumpPercent = 40 -BumpMin = '100 mwei' -[GasEstimator.BlockHistory] -BlockHistorySize = 12 +[GasEstimator.FeeHistory] +# Refresh the suggested price every 4 seconds, to stay slightly below their polling rate of 5s +CacheTimeout = '4s' [HeadTracker] # Polygon suffers from a tremendous number of re-orgs, we need to set this to something very large to be conservative enough diff --git a/ccip/config/evm/RSK_Mainnet.toml b/ccip/config/evm/RSK_Mainnet.toml new file mode 100644 index 00000000000..8290481a331 --- /dev/null +++ b/ccip/config/evm/RSK_Mainnet.toml @@ -0,0 +1,13 @@ +# RSK prices its txes in sats not wei +ChainID = '30' +LinkContractAddress = '0x14AdaE34beF7ca957Ce2dDe5ADD97ea050123827' +LogPollInterval = '30s' +MinContractPayment = '0.001 link' + +[GasEstimator] +# It's about 100 times more expensive than Wei, very roughly speaking +PriceDefault = '50 mwei' +PriceMax = '50 gwei' +PriceMin = '0' +# rsk does not yet support EIP-1559 but this allows validation to pass +FeeCapDefault = '100 mwei' diff --git a/ccip/config/evm/RSK_Testnet.toml b/ccip/config/evm/RSK_Testnet.toml new file mode 100644 index 00000000000..2fde16aa7cc --- /dev/null +++ b/ccip/config/evm/RSK_Testnet.toml @@ -0,0 +1,10 @@ +ChainID = '31' +LinkContractAddress = '0x8bBbd80981FE76d44854D8DF305e8985c19f0e78' +MinContractPayment = '0.001 link' +LogPollInterval = '30s' + +[GasEstimator] +PriceDefault = '50 mwei' +PriceMax = '50 gwei' +PriceMin = '0' +FeeCapDefault = '100 mwei' diff --git a/ccip/config/evm/Ronin_Mainnet.toml b/ccip/config/evm/Ronin_Mainnet.toml new file mode 100644 index 00000000000..14bb9d1e258 --- /dev/null +++ b/ccip/config/evm/Ronin_Mainnet.toml @@ -0,0 +1,16 @@ +ChainID = "2020" +FinalityTagEnabled = true +LinkContractAddress = "0x3902228D6A3d2Dc44731fD9d45FeE6a61c722D0b" +# Ronin produces blocks every 3 seconds +LogPollInterval = "3s" +NoNewHeadsThreshold = "3m" + +[GasEstimator] +# Ronin uses default gas price of 20 gwei https://docs.skymavis.com/mavis/mpc/guides/estimate-gas#overview +Mode = 'FeeHistory' +PriceMax = "1000 gwei" + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Ronin_Saigon.toml b/ccip/config/evm/Ronin_Saigon.toml new file mode 100644 index 00000000000..b775f8f0626 --- /dev/null +++ b/ccip/config/evm/Ronin_Saigon.toml @@ -0,0 +1,16 @@ +ChainID = "2021" +FinalityTagEnabled = true +LinkContractAddress = "0x5bB50A6888ee6a67E22afFDFD9513be7740F1c15" +# Ronin produces blocks every 3 seconds +LogPollInterval = "3s" +NoNewHeadsThreshold = "3m" + +[GasEstimator] +# Ronin uses default gas price of 20 gwei https://docs.skymavis.com/mavis/mpc/guides/estimate-gas#overview +Mode = 'FeeHistory' +PriceMax = "1000 gwei" + +[HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/Scroll_Mainnet.toml b/ccip/config/evm/Scroll_Mainnet.toml index b8e7bd09e80..f0449ef12be 100644 --- a/ccip/config/evm/Scroll_Mainnet.toml +++ b/ccip/config/evm/Scroll_Mainnet.toml @@ -17,6 +17,9 @@ BlockHistorySize = 24 [HeadTracker] HistoryDepth = 50 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [OCR] ContractConfirmations = 1 diff --git a/ccip/config/evm/Scroll_Sepolia.toml b/ccip/config/evm/Scroll_Sepolia.toml index baee2080d96..aca06ae18d3 100644 --- a/ccip/config/evm/Scroll_Sepolia.toml +++ b/ccip/config/evm/Scroll_Sepolia.toml @@ -17,6 +17,9 @@ BlockHistorySize = 24 [HeadTracker] HistoryDepth = 50 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [OCR] ContractConfirmations = 1 diff --git a/ccip/config/evm/Simulated.toml b/ccip/config/evm/Simulated.toml index e21dc0990f0..4ec8d962b21 100644 --- a/ccip/config/evm/Simulated.toml +++ b/ccip/config/evm/Simulated.toml @@ -1,5 +1,5 @@ ChainID = '1337' -FinalityDepth = 1 +FinalityDepth = 10 MinIncomingConfirmations = 1 MinContractPayment = '100' NoNewHeadsThreshold = '0s' @@ -19,7 +19,9 @@ PriceMax = '100 micro' HistoryDepth = 10 MaxBufferSize = 100 SamplingInterval = '0s' -PersistenceEnabled = false +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [OCR] ContractConfirmations = 1 diff --git a/ccip/config/evm/Soneium_Sepolia.toml b/ccip/config/evm/Soneium_Sepolia.toml new file mode 100755 index 00000000000..e0ea59ca22f --- /dev/null +++ b/ccip/config/evm/Soneium_Sepolia.toml @@ -0,0 +1,35 @@ +ChainID = '1946' +ChainType = 'optimismBedrock' +LinkContractAddress = '0x7ea13478Ea3961A0e8b538cb05a9DF0477c79Cd2' +FinalityDepth = 200 +LogPollInterval = '2s' +NoNewHeadsThreshold = '40s' +MinIncomingConfirmations = 1 +NoNewFinalizedHeadsThreshold = '120m' # Soneium can take upto 2Hours to finalize +FinalityTagEnabled = true + +[GasEstimator] +EIP1559DynamicFees = true +PriceMin = '1 wei' +BumpMin = '1 mwei' + +[GasEstimator.BlockHistory] +BlockHistorySize = 60 + +[Transactions] +ResendAfterThreshold = '30s' + +[HeadTracker] +HistoryDepth = 300 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false + +[NodePool] +SyncThreshold = 10 + +[OCR] +ContractConfirmations = 1 + +[OCR2.Automation] +GasLimit = 6500000 diff --git a/ccip/config/evm/Sonic_Mainnet.toml b/ccip/config/evm/Sonic_Mainnet.toml new file mode 100644 index 00000000000..523a931c8d6 --- /dev/null +++ b/ccip/config/evm/Sonic_Mainnet.toml @@ -0,0 +1,28 @@ +ChainId = '146' +FinalityDepth = 10 +FinalityTagEnabled = false +LogPollInterval = "1s" #1s block rate +MinIncomingConfirmations = 5 +RPCBlockQueryDelay = 10 +RPCDefaultBatchSize = 100 + +[GasEstimator] +Mode = 'FeeHistory' +EIP1559DynamicFees = true +BumpPercent = 10 +LimitDefault = 8000000 # default ccip value + +[GasEstimator.FeeHistory] +CacheTimeout = '2s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 + +[HeadTracker] +HistoryDepth = 50 + +[NodePool] +SyncThreshold = 10 + +[Transactions] +MaxQueued = 500 \ No newline at end of file diff --git a/ccip/config/evm/Sonic_Testnet.toml b/ccip/config/evm/Sonic_Testnet.toml new file mode 100644 index 00000000000..ca3ccf8f718 --- /dev/null +++ b/ccip/config/evm/Sonic_Testnet.toml @@ -0,0 +1,28 @@ +ChainId = '57054' +FinalityDepth = 10 +FinalityTagEnabled = false +LogPollInterval = "1s" #1s block rate +MinIncomingConfirmations = 5 +RPCBlockQueryDelay = 10 +RPCDefaultBatchSize = 100 + +[GasEstimator] +Mode = 'FeeHistory' +EIP1559DynamicFees = true +BumpPercent = 10 +LimitDefault = 8000000 # default ccip value + +[GasEstimator.FeeHistory] +CacheTimeout = '2s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 + +[HeadTracker] +HistoryDepth = 50 + +[NodePool] +SyncThreshold = 10 + +[Transactions] +MaxQueued = 500 \ No newline at end of file diff --git a/ccip/config/evm/Unichain_Testnet.toml b/ccip/config/evm/Unichain_Testnet.toml new file mode 100644 index 00000000000..5e18f0d4716 --- /dev/null +++ b/ccip/config/evm/Unichain_Testnet.toml @@ -0,0 +1,26 @@ +ChainID = '1301' +# OP stack: https://docs.unichain.org/docs/getting-started/set-up-a-node#overview +ChainType = 'optimismBedrock' +# finality_depth was: ~1900 +FinalityDepth = 2000 +# block_time was: ~1s, adding 1 second buffer +LogPollInterval = '2s' + +# batching_size_finalization_percentage = 30% according to the explorer batching view +# ( batching_size_finalization_percentage * finality_depth) * block_time / 60 secs = ~10 min (finality time) +# After running soak tests using 10m threw issues as there are batchs that take 35m, so we are bumping it to 45m to be sure +NoNewFinalizedHeadsThreshold = '45m' + +FinalityTagEnabled = true + +[GasEstimator] +EIP1559DynamicFees = true +Mode = 'FeeHistory' + +[GasEstimator.FeeHistory] +# block_time was: 1s, per recommendation skip 1-2 blocks +CacheTimeout = '2s' + +[GasEstimator.BlockHistory] +# As we see blocks containing between ~[8-12]tx, to get about ~1000 tx to check we would need to rougly go 100 tx back +BlockHistorySize = 100 diff --git a/ccip/config/evm/WeMix_Mainnet.toml b/ccip/config/evm/WeMix_Mainnet.toml index be7c278f692..a4e742d7300 100644 --- a/ccip/config/evm/WeMix_Mainnet.toml +++ b/ccip/config/evm/WeMix_Mainnet.toml @@ -16,4 +16,6 @@ EIP1559DynamicFees = true TipCapDefault = '100 gwei' [HeadTracker] -PersistenceEnabled = false +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false diff --git a/ccip/config/evm/WeMix_Testnet.toml b/ccip/config/evm/WeMix_Testnet.toml index 4591fc4c572..bfb75f158e3 100644 --- a/ccip/config/evm/WeMix_Testnet.toml +++ b/ccip/config/evm/WeMix_Testnet.toml @@ -16,5 +16,6 @@ EIP1559DynamicFees = true TipCapDefault = '100 gwei' [HeadTracker] +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 FinalityTagBypass = false -PersistenceEnabled = false diff --git a/ccip/config/evm/Worldchain_Mainnet.toml b/ccip/config/evm/Worldchain_Mainnet.toml new file mode 100644 index 00000000000..9b25d89d98c --- /dev/null +++ b/ccip/config/evm/Worldchain_Mainnet.toml @@ -0,0 +1,23 @@ +ChainID = '480' +# OP stack: https://worldcoin.notion.site/World-Chain-Developer-Preview-Guide-23c94a67683f4e71986e5303ab88c9f3 +ChainType = 'optimismBedrock' +# finality_depth was: ~2400 +FinalityDepth = 2500 +# block_time was: 2s, adding 1 second buffer +LogPollInterval = '3s' + +# finality_depth * block_time / 60 secs = ~83 min (finality time) +NoNewFinalizedHeadsThreshold = '90m' + +FinalityTagEnabled = true + +[GasEstimator] +EIP1559DynamicFees = true +Mode = 'FeeHistory' + +[GasEstimator.FeeHistory] +# block_time was: 2s, per recommendation skip 1-2 blocks +CacheTimeout = '4s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 diff --git a/ccip/config/evm/Worldchain_Testnet.toml b/ccip/config/evm/Worldchain_Testnet.toml new file mode 100644 index 00000000000..01618322285 --- /dev/null +++ b/ccip/config/evm/Worldchain_Testnet.toml @@ -0,0 +1,23 @@ +ChainID = '4801' +# OP stack: https://worldcoin.notion.site/World-Chain-Developer-Preview-Guide-23c94a67683f4e71986e5303ab88c9f3 +ChainType = 'optimismBedrock' +# finality_depth was: ~2400 +FinalityDepth = 2500 +# block_time was: 2s, adding 1 second buffer +LogPollInterval = '3s' + +# finality_depth * block_time / 60 secs = ~83 min (finality time) +NoNewFinalizedHeadsThreshold = '90m' + +FinalityTagEnabled = true + +[GasEstimator] +EIP1559DynamicFees = true +Mode = 'FeeHistory' + +[GasEstimator.FeeHistory] +# block_time was: 2s, per recommendation skip 1-2 blocks +CacheTimeout = '4s' + +[GasEstimator.BlockHistory] +BlockHistorySize = 100 diff --git a/ccip/config/evm/XLayer_Mainnet.toml b/ccip/config/evm/XLayer_Mainnet.toml index a39a9231ae2..28f7819276c 100644 --- a/ccip/config/evm/XLayer_Mainnet.toml +++ b/ccip/config/evm/XLayer_Mainnet.toml @@ -1,6 +1,6 @@ ChainID = '196' ChainType = 'xlayer' -FinalityDepth = 500 +FinalityDepth = 1000 NoNewHeadsThreshold = '6m' MinIncomingConfirmations = 1 LogPollInterval = '30s' diff --git a/ccip/config/evm/XLayer_Sepolia.toml b/ccip/config/evm/XLayer_Sepolia.toml index 2aa6e58469b..163d909542e 100644 --- a/ccip/config/evm/XLayer_Sepolia.toml +++ b/ccip/config/evm/XLayer_Sepolia.toml @@ -23,6 +23,9 @@ BlockHistorySize = 12 [HeadTracker] HistoryDepth = 2000 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false [Transactions.AutoPurge] Enabled = true diff --git a/ccip/config/evm/fallback.toml b/ccip/config/evm/fallback.toml new file mode 100644 index 00000000000..c1f963a33ff --- /dev/null +++ b/ccip/config/evm/fallback.toml @@ -0,0 +1,95 @@ +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +FinalityDepth = 50 +FinalityTagEnabled = false +LogBackfillBatchSize = 1000 +LogPollInterval = '15s' +LogKeepBlocksDepth = 100000 +# CCIP uses paging when removing logs to avoid pushing too much pressure on the database +LogPrunePageSize = 10000 +BackupLogPollerBlockDelay = 100 +MinContractPayment = '.00001 link' +MinIncomingConfirmations = 3 +NonceAutoSync = true +NoNewHeadsThreshold = '3m' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 +NoNewFinalizedHeadsThreshold = '0' +LogBroadcasterEnabled = true + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h' +ReaperThreshold = '168h' +ResendAfterThreshold = '1m' + +[Transactions.AutoPurge] +Enabled = false + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'BlockHistory' +PriceDefault = '20 gwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +PriceMin = '1 gwei' +LimitDefault = 8_000_000 +LimitMax = 8_000_000 +LimitMultiplier = '1' +LimitTransfer = 21_000 +BumpMin = '5 gwei' +BumpPercent = 20 +BumpThreshold = 3 +EIP1559DynamicFees = false +FeeCapDefault = '100 gwei' +TipCapDefault = '1' +TipCapMin = '1' +EstimateLimit = false + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 8 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[GasEstimator.FeeHistory] +CacheTimeout = '10s' + +[HeadTracker] +HistoryDepth = 100 +MaxBufferSize = 3 +SamplingInterval = '1s' +FinalityTagBypass = true +MaxAllowedFinalityDepth = 10000 + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 5 +LeaseDuration = '0s' +NodeIsSyncingEnabled = false +FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' +NewHeadsPollInterval = '0s' + +[OCR] +ContractConfirmations = 4 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +DeltaCOverride = '168h' +DeltaCJitterOverride = '1h' +ObservationGracePeriod = '1s' + +[OCR2.Automation] +GasLimit = 5400000 + +[Workflow] +GasLimitDefault = 400_000 diff --git a/ccip/config/evm/zkSync_Mainnet.toml b/ccip/config/evm/zkSync_Mainnet.toml index a8910a37e4a..a29098690b4 100644 --- a/ccip/config/evm/zkSync_Mainnet.toml +++ b/ccip/config/evm/zkSync_Mainnet.toml @@ -28,4 +28,4 @@ OracleType = 'zksync' [HeadTracker] # tracks top N blocks to keep in heads database table. Should store atleast the same # of blocks as finalityDepth -HistoryDepth = 1500 \ No newline at end of file +HistoryDepth = 1500 diff --git a/ccip/config/evm/zkSync_Sepolia.toml b/ccip/config/evm/zkSync_Sepolia.toml index 6eb4ba4137e..36b0c9282da 100644 --- a/ccip/config/evm/zkSync_Sepolia.toml +++ b/ccip/config/evm/zkSync_Sepolia.toml @@ -1,23 +1,23 @@ ChainID = '300' ChainType = 'zksync' # 200block ~ 20min concurrent with the l1_committed tag -FinalityDepth = 200 +FinalityDepth = 200 # block rate is ~2-5sec, so this ensures blocks are polled correctly LogPollInterval = '5s' # sufficient time for RPC to be labelled out of sync, since blockRate is pretty fast NoNewHeadsThreshold = '1m' [GasEstimator] -# no EIP1559 to ensure our estimator doesnot estimate gas with MaxPriorityFee which will break minFunding requirement -EIP1559DynamicFees = false -# high LimitDefault for worst case pubdata bytes with BatchGasLimit reduced to 4M in OCR2Config +# no EIP1559 to ensure our estimator doesnot estimate gas with MaxPriorityFee which will break minFunding requirement +EIP1559DynamicFees = false +# high LimitDefault for worst case pubdata bytes with BatchGasLimit reduced to 4M in OCR2Config LimitDefault = 2_500_000_000 FeeCapDefault = '500 mwei' PriceDefault = '25 mwei' # p999 value for gasPrice based on historical data PriceMax = '500 mwei' # avg gasPrices are at 0.025 gwei -PriceMin = '25 mwei' +PriceMin = '25 mwei' [GasEstimator.BlockHistory] # increasing this to smooth out gas estimation @@ -28,4 +28,7 @@ OracleType = 'zksync' [HeadTracker] # tracks top N blocks to keep in heads database table. Should store atleast the same # of blocks as finalityDepth -HistoryDepth = 250 \ No newline at end of file +HistoryDepth = 250 +# FinalityDepth < 1k => FinalityTagBypass = false +# https://smartcontract-it.atlassian.net/browse/SHIP-4078 +FinalityTagBypass = false From d2cb00789be0690e62e7dc8db0220218dae99ce7 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 8 Jan 2025 12:46:01 -0500 Subject: [PATCH 3/7] Remove panic recovery for wsrpc (#15865) - Due to some other bug in the library, the redialled connection never becomes ready. --- .../relay/evm/mercury/wsrpc/client.go | 14 ---- .../relay/evm/mercury/wsrpc/client_test.go | 66 ------------------- 2 files changed, 80 deletions(-) diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go index ebd282e6093..1cd8c9af50d 100644 --- a/core/services/relay/evm/mercury/wsrpc/client.go +++ b/core/services/relay/evm/mercury/wsrpc/client.go @@ -286,13 +286,6 @@ func (w *client) waitForReady(ctx context.Context) (err error) { func (w *client) Transmit(ctx context.Context, req *pb.TransmitRequest) (resp *pb.TransmitResponse, err error) { ok := w.IfStarted(func() { - defer func() { - if r := recover(); r != nil { - w.handlePanic(r) - resp = nil - err = fmt.Errorf("Transmit: caught panic: %v", r) - } - }() w.logger.Trace("Transmit") start := time.Now() if err = w.waitForReady(ctx); err != nil { @@ -360,13 +353,6 @@ func (w *client) handleTimeout(err error) { func (w *client) LatestReport(ctx context.Context, req *pb.LatestReportRequest) (resp *pb.LatestReportResponse, err error) { ok := w.IfStarted(func() { - defer func() { - if r := recover(); r != nil { - w.handlePanic(r) - resp = nil - err = fmt.Errorf("LatestReport: caught panic: %v", r) - } - }() lggr := w.logger.With("req.FeedId", hexutil.Encode(req.FeedId)) lggr.Trace("LatestReport") if err = w.waitForReady(ctx); err != nil { diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go index b2bbf074e91..7c6706eddf8 100644 --- a/core/services/relay/evm/mercury/wsrpc/client_test.go +++ b/core/services/relay/evm/mercury/wsrpc/client_test.go @@ -2,16 +2,11 @@ package wsrpc import ( "context" - "math/big" - "math/rand" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - grpc_connectivity "google.golang.org/grpc/connectivity" - - "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -128,67 +123,6 @@ func Test_Client_Transmit(t *testing.T) { } }) }) - - t.Run("recovers panics in underlying client and attempts redial", func(t *testing.T) { - makeConn := func() *mocks.MockConn { - return &mocks.MockConn{ - Ready: true, - State: grpc_connectivity.Ready, - InvokeF: func(ctx context.Context, method string, args interface{}, reply interface{}) error { - panic("TESTING CONN INVOKE PANIC") - }, - } - } - - ch := make(chan *mocks.MockConn, 100) - cnt := 0 - - f := func(ctxCaller context.Context, target string, opts ...wsrpc.DialOption) (Conn, error) { - cnt++ - switch cnt { - case 1, 2: - conn := makeConn() - ch <- conn - return conn, nil - default: - t.Fatalf("too many dials, got: %d", cnt) - return nil, nil - } - } - - clientKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(rand.Int63())) - serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(rand.Int63())) - opts := ClientOpts{ - lggr, - clientKey, - serverKey.PublicKey, - "", - noopCacheSet, - f, - } - c := newClient(opts) - - servicetest.Run(t, c) - - // drain the channel - var conn *mocks.MockConn - select { - case conn = <-ch: - assert.Equal(t, 1, cnt) - default: - t.Fatalf("expected dial to be called") - } - - _, err := c.Transmit(ctx, req) - require.EqualError(t, err, "Transmit: caught panic: TESTING CONN INVOKE PANIC") - - // expect conn to be closed and re-dialed - conn2 := <-ch - assert.Equal(t, 2, cnt) - - assert.True(t, conn.Closed) - assert.False(t, conn2.Closed) - }) } func Test_Client_LatestReport(t *testing.T) { From 0747d30778e62cbd5bd913f8754f3c70f1ffb6ea Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:17:42 +0100 Subject: [PATCH 4/7] Added is_backfiled filed to solana's filter table (#15796) --- .../0263_solana_filter_is_backfilled.sql | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 core/store/migrate/migrations/0263_solana_filter_is_backfilled.sql diff --git a/core/store/migrate/migrations/0263_solana_filter_is_backfilled.sql b/core/store/migrate/migrations/0263_solana_filter_is_backfilled.sql new file mode 100644 index 00000000000..7e81e7c4c5e --- /dev/null +++ b/core/store/migrate/migrations/0263_solana_filter_is_backfilled.sql @@ -0,0 +1,14 @@ +-- +goose Up +-- +goose StatementBegin + +ALTER TABLE solana.log_poller_filters ADD COLUMN is_backfilled BOOLEAN; +UPDATE solana.log_poller_filters SET is_backfilled = true; +ALTER TABLE solana.log_poller_filters ALTER COLUMN is_backfilled SET NOT NULL; +-- +goose StatementEnd + + +-- +goose Down +-- +goose StatementBegin + +ALTER TABLE solana.log_poller_filters DROP COLUMN is_backfilled; +-- +goose StatementEnd From 2450fff71db772d7e771babb5cbe1a55f5a51f84 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Wed, 8 Jan 2025 13:43:31 -0500 Subject: [PATCH 5/7] Extract MultiNode to chainlink-framework (#15791) * Extract MultiNode * tidy * tidy * Fix generate * Add mock Subscription * lint * Fix sendonly allocation * lint * Add QueryTimeout to client * Use multinode * Update rpc_client_test.go --- .changeset/cold-pillows-sleep.md | 5 + .mockery.yaml | 20 +- common/client/ctx.go | 17 - common/client/ctx_test.go | 16 - common/client/mock_hashable_test.go | 18 - common/client/mock_head_test.go | 173 -- common/client/mock_node_selector_test.go | 127 -- common/client/mock_node_test.go | 567 ----- .../mock_pool_chain_info_provider_test.go | 132 -- common/client/mock_rpc_client_test.go | 510 ----- common/client/mock_send_only_client_test.go | 173 -- common/client/mock_send_only_node_test.go | 357 --- common/client/mocks/config.go | 31 - common/client/models.go | 121 - common/client/models_test.go | 50 - common/client/multi_node.go | 364 --- common/client/multi_node_test.go | 517 ----- common/client/node.go | 336 --- common/client/node_fsm.go | 377 ---- common/client/node_fsm_test.go | 131 -- common/client/node_lifecycle.go | 700 ------ common/client/node_lifecycle_test.go | 1983 ----------------- common/client/node_selector.go | 43 - common/client/node_selector_highest_head.go | 40 - .../client/node_selector_highest_head_test.go | 176 -- common/client/node_selector_priority_level.go | 123 - .../node_selector_priority_level_test.go | 91 - common/client/node_selector_round_robin.go | 48 - .../client/node_selector_round_robin_test.go | 61 - common/client/node_selector_test.go | 18 - .../client/node_selector_total_difficulty.go | 53 - .../node_selector_total_difficulty_test.go | 178 -- common/client/node_test.go | 107 - common/client/poller.go | 95 - common/client/poller_test.go | 194 -- common/client/send_only_node.go | 183 -- common/client/send_only_node_lifecycle.go | 67 - common/client/send_only_node_test.go | 139 -- common/client/timeout.go | 5 + common/client/transaction_sender.go | 301 --- common/client/transaction_sender_test.go | 419 ---- common/client/types.go | 83 - common/client/types_test.go | 34 - common/txmgr/broadcaster.go | 34 +- common/txmgr/confirmer.go | 20 +- common/txmgr/resender.go | 8 +- common/txmgr/types/client.go | 7 +- common/types/mocks/head.go | 607 ----- core/chains/evm/client/chain_client.go | 18 +- core/chains/evm/client/chain_client_test.go | 82 +- core/chains/evm/client/config_builder.go | 4 +- core/chains/evm/client/errors.go | 42 +- core/chains/evm/client/errors_test.go | 7 +- core/chains/evm/client/evm_client.go | 15 +- core/chains/evm/client/helpers_test.go | 29 +- core/chains/evm/client/mocks/client.go | 18 +- core/chains/evm/client/null_client.go | 6 +- core/chains/evm/client/rpc_client.go | 51 +- .../evm/client/rpc_client_internal_test.go | 5 +- core/chains/evm/client/rpc_client_test.go | 54 +- .../evm/client/simulated_backend_client.go | 10 +- core/chains/evm/txmgr/broadcaster_test.go | 88 +- core/chains/evm/txmgr/client.go | 10 +- core/chains/evm/txmgr/confirmer_test.go | 78 +- core/cmd/shell_local_test.go | 8 +- core/internal/cltest/cltest.go | 4 +- core/scripts/go.mod | 9 +- core/scripts/go.sum | 18 +- core/services/chainlink/config_test.go | 6 +- deployment/go.mod | 9 +- deployment/go.sum | 18 +- go.md | 6 + go.mod | 9 +- go.sum | 18 +- integration-tests/go.mod | 9 +- integration-tests/go.sum | 18 +- integration-tests/load/go.mod | 9 +- integration-tests/load/go.sum | 18 +- 78 files changed, 399 insertions(+), 10136 deletions(-) create mode 100644 .changeset/cold-pillows-sleep.md delete mode 100644 common/client/ctx.go delete mode 100644 common/client/ctx_test.go delete mode 100644 common/client/mock_hashable_test.go delete mode 100644 common/client/mock_head_test.go delete mode 100644 common/client/mock_node_selector_test.go delete mode 100644 common/client/mock_node_test.go delete mode 100644 common/client/mock_pool_chain_info_provider_test.go delete mode 100644 common/client/mock_rpc_client_test.go delete mode 100644 common/client/mock_send_only_client_test.go delete mode 100644 common/client/mock_send_only_node_test.go delete mode 100644 common/client/mocks/config.go delete mode 100644 common/client/models.go delete mode 100644 common/client/models_test.go delete mode 100644 common/client/multi_node.go delete mode 100644 common/client/multi_node_test.go delete mode 100644 common/client/node.go delete mode 100644 common/client/node_fsm.go delete mode 100644 common/client/node_fsm_test.go delete mode 100644 common/client/node_lifecycle.go delete mode 100644 common/client/node_lifecycle_test.go delete mode 100644 common/client/node_selector.go delete mode 100644 common/client/node_selector_highest_head.go delete mode 100644 common/client/node_selector_highest_head_test.go delete mode 100644 common/client/node_selector_priority_level.go delete mode 100644 common/client/node_selector_priority_level_test.go delete mode 100644 common/client/node_selector_round_robin.go delete mode 100644 common/client/node_selector_round_robin_test.go delete mode 100644 common/client/node_selector_test.go delete mode 100644 common/client/node_selector_total_difficulty.go delete mode 100644 common/client/node_selector_total_difficulty_test.go delete mode 100644 common/client/node_test.go delete mode 100644 common/client/poller.go delete mode 100644 common/client/poller_test.go delete mode 100644 common/client/send_only_node.go delete mode 100644 common/client/send_only_node_lifecycle.go delete mode 100644 common/client/send_only_node_test.go create mode 100644 common/client/timeout.go delete mode 100644 common/client/transaction_sender.go delete mode 100644 common/client/transaction_sender_test.go delete mode 100644 common/client/types.go delete mode 100644 common/client/types_test.go delete mode 100644 common/types/mocks/head.go diff --git a/.changeset/cold-pillows-sleep.md b/.changeset/cold-pillows-sleep.md new file mode 100644 index 00000000000..45e4e999111 --- /dev/null +++ b/.changeset/cold-pillows-sleep.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +Extract EVM MultiNode to chainlink-framework. #internal diff --git a/.mockery.yaml b/.mockery.yaml index 7efde733145..73f46deed57 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -3,20 +3,6 @@ mockname: "{{ .InterfaceName }}" outpkg: mocks filename: "{{ .InterfaceName | snakecase }}.go" packages: - github.com/smartcontractkit/chainlink/v2/common/client: - config: - dir: "{{ .InterfaceDir }}" - filename: "mock_{{ .InterfaceName | snakecase }}_test.go" - inpackage: true - mockname: "mock{{ .InterfaceName | camelcase }}" - interfaces: - Node: - NodeSelector: - sendOnlyClient: - SendOnlyNode: - RPCClient: - Head: - PoolChainInfoProvider: github.com/smartcontractkit/chainlink/v2/common/headtracker: interfaces: HeadTrackable: @@ -35,9 +21,11 @@ packages: TxStrategy: TxAttemptBuilder: TxStore: - github.com/smartcontractkit/chainlink/v2/common/types: + github.com/smartcontractkit/chainlink-framework/multinode: + config: + dir: common/types/mocks + outpkg: mocks interfaces: - Head: Subscription: github.com/smartcontractkit/chainlink/v2/core/bridges: interfaces: diff --git a/common/client/ctx.go b/common/client/ctx.go deleted file mode 100644 index 57b2fc8a866..00000000000 --- a/common/client/ctx.go +++ /dev/null @@ -1,17 +0,0 @@ -package client - -import "context" - -type multiNodeContextKey int - -const ( - contextKeyHeathCheckRequest multiNodeContextKey = iota + 1 -) - -func CtxAddHealthCheckFlag(ctx context.Context) context.Context { - return context.WithValue(ctx, contextKeyHeathCheckRequest, struct{}{}) -} - -func CtxIsHeathCheckRequest(ctx context.Context) bool { - return ctx.Value(contextKeyHeathCheckRequest) != nil -} diff --git a/common/client/ctx_test.go b/common/client/ctx_test.go deleted file mode 100644 index 822b36c3f81..00000000000 --- a/common/client/ctx_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" -) - -func TestContext(t *testing.T) { - ctx := tests.Context(t) - assert.False(t, CtxIsHeathCheckRequest(ctx), "expected false for test context") - ctx = CtxAddHealthCheckFlag(ctx) - assert.True(t, CtxIsHeathCheckRequest(ctx), "expected context to contain the healthcheck flag") -} diff --git a/common/client/mock_hashable_test.go b/common/client/mock_hashable_test.go deleted file mode 100644 index d9f1670c073..00000000000 --- a/common/client/mock_hashable_test.go +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index 01884d3fcfc..00000000000 --- a/common/client/mock_head_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package client - -import ( - big "math/big" - - mock "github.com/stretchr/testify/mock" -) - -// mockHead is an autogenerated mock type for the Head type -type mockHead struct { - mock.Mock -} - -type mockHead_Expecter struct { - mock *mock.Mock -} - -func (_m *mockHead) EXPECT() *mockHead_Expecter { - return &mockHead_Expecter{mock: &_m.Mock} -} - -// BlockDifficulty provides a mock function with no fields -func (_m *mockHead) BlockDifficulty() *big.Int { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for BlockDifficulty") - } - - 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).(*big.Int) - } - } - - return r0 -} - -// mockHead_BlockDifficulty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockDifficulty' -type mockHead_BlockDifficulty_Call struct { - *mock.Call -} - -// BlockDifficulty is a helper method to define mock.On call -func (_e *mockHead_Expecter) BlockDifficulty() *mockHead_BlockDifficulty_Call { - return &mockHead_BlockDifficulty_Call{Call: _e.mock.On("BlockDifficulty")} -} - -func (_c *mockHead_BlockDifficulty_Call) Run(run func()) *mockHead_BlockDifficulty_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockHead_BlockDifficulty_Call) Return(_a0 *big.Int) *mockHead_BlockDifficulty_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockHead_BlockDifficulty_Call) RunAndReturn(run func() *big.Int) *mockHead_BlockDifficulty_Call { - _c.Call.Return(run) - return _c -} - -// BlockNumber provides a mock function with no fields -func (_m *mockHead) BlockNumber() int64 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for BlockNumber") - } - - var r0 int64 - if rf, ok := ret.Get(0).(func() int64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int64) - } - - return r0 -} - -// mockHead_BlockNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockNumber' -type mockHead_BlockNumber_Call struct { - *mock.Call -} - -// BlockNumber is a helper method to define mock.On call -func (_e *mockHead_Expecter) BlockNumber() *mockHead_BlockNumber_Call { - return &mockHead_BlockNumber_Call{Call: _e.mock.On("BlockNumber")} -} - -func (_c *mockHead_BlockNumber_Call) Run(run func()) *mockHead_BlockNumber_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockHead_BlockNumber_Call) Return(_a0 int64) *mockHead_BlockNumber_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockHead_BlockNumber_Call) RunAndReturn(run func() int64) *mockHead_BlockNumber_Call { - _c.Call.Return(run) - return _c -} - -// IsValid provides a mock function with no fields -func (_m *mockHead) IsValid() bool { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for IsValid") - } - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// mockHead_IsValid_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsValid' -type mockHead_IsValid_Call struct { - *mock.Call -} - -// IsValid is a helper method to define mock.On call -func (_e *mockHead_Expecter) IsValid() *mockHead_IsValid_Call { - return &mockHead_IsValid_Call{Call: _e.mock.On("IsValid")} -} - -func (_c *mockHead_IsValid_Call) Run(run func()) *mockHead_IsValid_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockHead_IsValid_Call) Return(_a0 bool) *mockHead_IsValid_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockHead_IsValid_Call) RunAndReturn(run func() bool) *mockHead_IsValid_Call { - _c.Call.Return(run) - return _c -} - -// 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_selector_test.go b/common/client/mock_node_selector_test.go deleted file mode 100644 index c4201664b4a..00000000000 --- a/common/client/mock_node_selector_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// Code generated by mockery v2.50.0. 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, RPC interface{}] struct { - mock.Mock -} - -type mockNodeSelector_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { - mock *mock.Mock -} - -func (_m *mockNodeSelector[CHAIN_ID, RPC]) EXPECT() *mockNodeSelector_Expecter[CHAIN_ID, RPC] { - return &mockNodeSelector_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock} -} - -// Name provides a mock function with no fields -func (_m *mockNodeSelector[CHAIN_ID, RPC]) Name() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Name") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// mockNodeSelector_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' -type mockNodeSelector_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Name is a helper method to define mock.On call -func (_e *mockNodeSelector_Expecter[CHAIN_ID, RPC]) Name() *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { - return &mockNodeSelector_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")} -} - -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// Select provides a mock function with no fields -func (_m *mockNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Select") - } - - var r0 Node[CHAIN_ID, RPC] - if rf, ok := ret.Get(0).(func() Node[CHAIN_ID, RPC]); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(Node[CHAIN_ID, RPC]) - } - } - - return r0 -} - -// mockNodeSelector_Select_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Select' -type mockNodeSelector_Select_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Select is a helper method to define mock.On call -func (_e *mockNodeSelector_Expecter[CHAIN_ID, RPC]) Select() *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { - return &mockNodeSelector_Select_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Select")} -} - -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) Return(_a0 Node[CHAIN_ID, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) RunAndReturn(run func() Node[CHAIN_ID, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// 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, RPC interface{}](t interface { - mock.TestingT - Cleanup(func()) -}) *mockNodeSelector[CHAIN_ID, RPC] { - mock := &mockNodeSelector[CHAIN_ID, 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 deleted file mode 100644 index f0fc6a4cb58..00000000000 --- a/common/client/mock_node_test.go +++ /dev/null @@ -1,567 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package client - -import ( - context "context" - - types "github.com/smartcontractkit/chainlink/v2/common/types" - mock "github.com/stretchr/testify/mock" -) - -// mockNode is an autogenerated mock type for the Node type -type mockNode[CHAIN_ID types.ID, RPC interface{}] struct { - mock.Mock -} - -type mockNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { - mock *mock.Mock -} - -func (_m *mockNode[CHAIN_ID, RPC]) EXPECT() *mockNode_Expecter[CHAIN_ID, RPC] { - return &mockNode_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Close() *mockNode_Close_Call[CHAIN_ID, RPC] { - return &mockNode_Close_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Close")} -} - -func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Close_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockNode_Close_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) RunAndReturn(run func() error) *mockNode_Close_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// ConfiguredChainID provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ConfiguredChainID") - } - - var r0 CHAIN_ID - if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(CHAIN_ID) - } - } - - return r0 -} - -// mockNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID' -type mockNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// ConfiguredChainID is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) ConfiguredChainID() *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { - return &mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]{Call: _e.mock.On("ConfiguredChainID")} -} - -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Return(_a0 CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) RunAndReturn(run func() CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// HighestUserObservations provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for HighestUserObservations") - } - - var r0 ChainInfo - if rf, ok := ret.Get(0).(func() ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ChainInfo) - } - - return r0 -} - -// mockNode_HighestUserObservations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HighestUserObservations' -type mockNode_HighestUserObservations_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// HighestUserObservations is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) HighestUserObservations() *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { - return &mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]{Call: _e.mock.On("HighestUserObservations")} -} - -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) Return(_a0 ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) RunAndReturn(run func() ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// Name provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) Name() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Name") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// mockNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' -type mockNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Name is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Name() *mockNode_Name_Call[CHAIN_ID, RPC] { - return &mockNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")} -} - -func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Name_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNode_Name_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNode_Name_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// Order provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) Order() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Order") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// mockNode_Order_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Order' -type mockNode_Order_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Order is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Order() *mockNode_Order_Call[CHAIN_ID, RPC] { - return &mockNode_Order_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Order")} -} - -func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Order_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) Return(_a0 int32) *mockNode_Order_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) RunAndReturn(run func() int32) *mockNode_Order_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// RPC provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) RPC() RPC { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for RPC") - } - - var r0 RPC - if rf, ok := ret.Get(0).(func() RPC); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(RPC) - } - } - - return r0 -} - -// mockNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC' -type mockNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// RPC is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) RPC() *mockNode_RPC_Call[CHAIN_ID, RPC] { - return &mockNode_RPC_Call[CHAIN_ID, RPC]{Call: _e.mock.On("RPC")} -} - -func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_RPC_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) Return(_a0 RPC) *mockNode_RPC_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) RunAndReturn(run func() RPC) *mockNode_RPC_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// SetPoolChainInfoProvider provides a mock function with given fields: _a0 -func (_m *mockNode[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoProvider) { - _m.Called(_a0) -} - -// mockNode_SetPoolChainInfoProvider_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPoolChainInfoProvider' -type mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// SetPoolChainInfoProvider is a helper method to define mock.On call -// - _a0 PoolChainInfoProvider -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 interface{}) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { - return &mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]{Call: _e.mock.On("SetPoolChainInfoProvider", _a0)} -} - -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Run(run func(_a0 PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(PoolChainInfoProvider)) - }) - return _c -} - -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Return() *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { - _c.Call.Return() - return _c -} - -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) RunAndReturn(run func(PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { - _c.Run(run) - return _c -} - -// Start provides a mock function with given fields: _a0 -func (_m *mockNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for Start") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type mockNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Start is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Start(_a0 interface{}) *mockNode_Start_Call[CHAIN_ID, RPC] { - return &mockNode_Start_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Start", _a0)} -} - -func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) Run(run func(_a0 context.Context)) *mockNode_Start_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockNode_Start_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) RunAndReturn(run func(context.Context) error) *mockNode_Start_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// State provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) State() nodeState { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for State") - } - - var r0 nodeState - if rf, ok := ret.Get(0).(func() nodeState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(nodeState) - } - - return r0 -} - -// mockNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type mockNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// State is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) State() *mockNode_State_Call[CHAIN_ID, RPC] { - return &mockNode_State_Call[CHAIN_ID, RPC]{Call: _e.mock.On("State")} -} - -func (_c *mockNode_State_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_State_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_State_Call[CHAIN_ID, RPC]) Return(_a0 nodeState) *mockNode_State_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_State_Call[CHAIN_ID, RPC]) RunAndReturn(run func() nodeState) *mockNode_State_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// StateAndLatest provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) StateAndLatest() (nodeState, ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for StateAndLatest") - } - - var r0 nodeState - var r1 ChainInfo - if rf, ok := ret.Get(0).(func() (nodeState, ChainInfo)); 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() ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(ChainInfo) - } - - return r0, r1 -} - -// mockNode_StateAndLatest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StateAndLatest' -type mockNode_StateAndLatest_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// StateAndLatest is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) StateAndLatest() *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { - return &mockNode_StateAndLatest_Call[CHAIN_ID, RPC]{Call: _e.mock.On("StateAndLatest")} -} - -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) Return(_a0 nodeState, _a1 ChainInfo) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) RunAndReturn(run func() (nodeState, ChainInfo)) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// String provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) String() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for String") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// mockNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' -type mockNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// String is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) String() *mockNode_String_Call[CHAIN_ID, RPC] { - return &mockNode_String_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")} -} - -func (_c *mockNode_String_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_String_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_String_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNode_String_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNode_String_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// UnsubscribeAllExceptAliveLoop provides a mock function with no fields -func (_m *mockNode[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() { - _m.Called() -} - -// mockNode_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { - return &mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} -} - -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Return() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { - _c.Call.Return() - return _c -} - -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) RunAndReturn(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { - _c.Run(run) - return _c -} - -// 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, RPC interface{}](t interface { - mock.TestingT - Cleanup(func()) -}) *mockNode[CHAIN_ID, RPC] { - mock := &mockNode[CHAIN_ID, RPC]{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/common/client/mock_pool_chain_info_provider_test.go b/common/client/mock_pool_chain_info_provider_test.go deleted file mode 100644 index 7523a060329..00000000000 --- a/common/client/mock_pool_chain_info_provider_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package client - -import mock "github.com/stretchr/testify/mock" - -// mockPoolChainInfoProvider is an autogenerated mock type for the PoolChainInfoProvider type -type mockPoolChainInfoProvider struct { - mock.Mock -} - -type mockPoolChainInfoProvider_Expecter struct { - mock *mock.Mock -} - -func (_m *mockPoolChainInfoProvider) EXPECT() *mockPoolChainInfoProvider_Expecter { - return &mockPoolChainInfoProvider_Expecter{mock: &_m.Mock} -} - -// HighestUserObservations provides a mock function with no fields -func (_m *mockPoolChainInfoProvider) HighestUserObservations() ChainInfo { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for HighestUserObservations") - } - - var r0 ChainInfo - if rf, ok := ret.Get(0).(func() ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ChainInfo) - } - - return r0 -} - -// mockPoolChainInfoProvider_HighestUserObservations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HighestUserObservations' -type mockPoolChainInfoProvider_HighestUserObservations_Call struct { - *mock.Call -} - -// HighestUserObservations is a helper method to define mock.On call -func (_e *mockPoolChainInfoProvider_Expecter) HighestUserObservations() *mockPoolChainInfoProvider_HighestUserObservations_Call { - return &mockPoolChainInfoProvider_HighestUserObservations_Call{Call: _e.mock.On("HighestUserObservations")} -} - -func (_c *mockPoolChainInfoProvider_HighestUserObservations_Call) Run(run func()) *mockPoolChainInfoProvider_HighestUserObservations_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockPoolChainInfoProvider_HighestUserObservations_Call) Return(_a0 ChainInfo) *mockPoolChainInfoProvider_HighestUserObservations_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockPoolChainInfoProvider_HighestUserObservations_Call) RunAndReturn(run func() ChainInfo) *mockPoolChainInfoProvider_HighestUserObservations_Call { - _c.Call.Return(run) - return _c -} - -// LatestChainInfo provides a mock function with no fields -func (_m *mockPoolChainInfoProvider) LatestChainInfo() (int, ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for LatestChainInfo") - } - - var r0 int - var r1 ChainInfo - if rf, ok := ret.Get(0).(func() (int, ChainInfo)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - if rf, ok := ret.Get(1).(func() ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(ChainInfo) - } - - return r0, r1 -} - -// mockPoolChainInfoProvider_LatestChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestChainInfo' -type mockPoolChainInfoProvider_LatestChainInfo_Call struct { - *mock.Call -} - -// LatestChainInfo is a helper method to define mock.On call -func (_e *mockPoolChainInfoProvider_Expecter) LatestChainInfo() *mockPoolChainInfoProvider_LatestChainInfo_Call { - return &mockPoolChainInfoProvider_LatestChainInfo_Call{Call: _e.mock.On("LatestChainInfo")} -} - -func (_c *mockPoolChainInfoProvider_LatestChainInfo_Call) Run(run func()) *mockPoolChainInfoProvider_LatestChainInfo_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockPoolChainInfoProvider_LatestChainInfo_Call) Return(_a0 int, _a1 ChainInfo) *mockPoolChainInfoProvider_LatestChainInfo_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockPoolChainInfoProvider_LatestChainInfo_Call) RunAndReturn(run func() (int, ChainInfo)) *mockPoolChainInfoProvider_LatestChainInfo_Call { - _c.Call.Return(run) - return _c -} - -// newMockPoolChainInfoProvider creates a new instance of mockPoolChainInfoProvider. 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 newMockPoolChainInfoProvider(t interface { - mock.TestingT - Cleanup(func()) -}) *mockPoolChainInfoProvider { - mock := &mockPoolChainInfoProvider{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/common/client/mock_rpc_client_test.go b/common/client/mock_rpc_client_test.go deleted file mode 100644 index 9ad71c646e4..00000000000 --- a/common/client/mock_rpc_client_test.go +++ /dev/null @@ -1,510 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package client - -import ( - context "context" - - types "github.com/smartcontractkit/chainlink/v2/common/types" - mock "github.com/stretchr/testify/mock" -) - -// mockRPCClient is an autogenerated mock type for the RPCClient type -type mockRPCClient[CHAIN_ID types.ID, HEAD Head] struct { - mock.Mock -} - -type mockRPCClient_Expecter[CHAIN_ID types.ID, HEAD Head] struct { - mock *mock.Mock -} - -func (_m *mockRPCClient[CHAIN_ID, HEAD]) EXPECT() *mockRPCClient_Expecter[CHAIN_ID, HEAD] { - return &mockRPCClient_Expecter[CHAIN_ID, HEAD]{mock: &_m.Mock} -} - -// ChainID provides a mock function with given fields: ctx -func (_m *mockRPCClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - 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 { - if ret.Get(0) != nil { - 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 -} - -// mockRPCClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type mockRPCClient_ChainID_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) ChainID(ctx interface{}) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("ChainID", ctx)} -} - -func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) Return(_a0 CHAIN_ID, _a1 error) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with no fields -func (_m *mockRPCClient[CHAIN_ID, HEAD]) Close() { - _m.Called() -} - -// mockRPCClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockRPCClient_Close_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Close() *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_Close_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Close")} -} - -func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Run(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { - _c.Run(run) - return _c -} - -// Dial provides a mock function with given fields: ctx -func (_m *mockRPCClient[CHAIN_ID, HEAD]) Dial(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPCClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' -type mockRPCClient_Dial_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// Dial is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Dial(ctx interface{}) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_Dial_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Dial", ctx)} -} - -func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// GetInterceptedChainInfo provides a mock function with no fields -func (_m *mockRPCClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetInterceptedChainInfo") - } - - var r0 ChainInfo - var r1 ChainInfo - if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ChainInfo) - } - - if rf, ok := ret.Get(1).(func() ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(ChainInfo) - } - - return r0, r1 -} - -// mockRPCClient_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' -type mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// GetInterceptedChainInfo is a helper method to define mock.On call -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) GetInterceptedChainInfo() *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("GetInterceptedChainInfo")} -} - -func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Run(run func()) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Return(latest ChainInfo, highestUserObservations ChainInfo) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Return(latest, highestUserObservations) - return _c -} - -func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() (ChainInfo, ChainInfo)) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// IsSyncing provides a mock function with given fields: ctx -func (_m *mockRPCClient[CHAIN_ID, HEAD]) IsSyncing(ctx context.Context) (bool, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for IsSyncing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) bool); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPCClient_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' -type mockRPCClient_IsSyncing_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// IsSyncing is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) IsSyncing(ctx interface{}) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("IsSyncing", ctx)} -} - -func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) Return(_a0 bool, _a1 error) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (bool, error)) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// Ping provides a mock function with given fields: _a0 -func (_m *mockRPCClient[CHAIN_ID, HEAD]) Ping(_a0 context.Context) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for Ping") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPCClient_Ping_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Ping' -type mockRPCClient_Ping_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// Ping is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Ping(_a0 interface{}) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_Ping_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Ping", _a0)} -} - -func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) Run(run func(_a0 context.Context)) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribeToFinalizedHeads provides a mock function with given fields: ctx -func (_m *mockRPCClient[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToFinalizedHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(ctx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(ctx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockRPCClient_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' -type mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribeToFinalizedHeads is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(ctx interface{}) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToFinalizedHeads", ctx)} -} - -func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribeToHeads provides a mock function with given fields: ctx -func (_m *mockRPCClient[CHAIN_ID, HEAD]) SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(ctx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(ctx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockRPCClient_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' -type mockRPCClient_SubscribeToHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribeToHeads is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) SubscribeToHeads(ctx interface{}) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToHeads", ctx)} -} - -func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// UnsubscribeAllExcept provides a mock function with given fields: subs -func (_m *mockRPCClient[CHAIN_ID, HEAD]) UnsubscribeAllExcept(subs ...types.Subscription) { - _va := make([]interface{}, len(subs)) - for _i := range subs { - _va[_i] = subs[_i] - } - var _ca []interface{} - _ca = append(_ca, _va...) - _m.Called(_ca...) -} - -// mockRPCClient_UnsubscribeAllExcept_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExcept' -type mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// UnsubscribeAllExcept is a helper method to define mock.On call -// - subs ...types.Subscription -func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) UnsubscribeAllExcept(subs ...interface{}) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { - return &mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("UnsubscribeAllExcept", - append([]interface{}{}, subs...)...)} -} - -func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Run(run func(subs ...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]types.Subscription, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(types.Subscription) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { - _c.Run(run) - return _c -} - -// newMockRPCClient creates a new instance of mockRPCClient. 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 newMockRPCClient[CHAIN_ID types.ID, HEAD Head](t interface { - mock.TestingT - Cleanup(func()) -}) *mockRPCClient[CHAIN_ID, HEAD] { - mock := &mockRPCClient[CHAIN_ID, 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 deleted file mode 100644 index 0def3c58a2e..00000000000 --- a/common/client/mock_send_only_client_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Code generated by mockery v2.50.0. 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 -} - -type mockSendOnlyClient_Expecter[CHAIN_ID types.ID] struct { - mock *mock.Mock -} - -func (_m *mockSendOnlyClient[CHAIN_ID]) EXPECT() *mockSendOnlyClient_Expecter[CHAIN_ID] { - return &mockSendOnlyClient_Expecter[CHAIN_ID]{mock: &_m.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) - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - 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 { - if ret.Get(0) != nil { - 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 -} - -// mockSendOnlyClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type mockSendOnlyClient_ChainID_Call[CHAIN_ID types.ID] struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) ChainID(_a0 interface{}) *mockSendOnlyClient_ChainID_Call[CHAIN_ID] { - return &mockSendOnlyClient_ChainID_Call[CHAIN_ID]{Call: _e.mock.On("ChainID", _a0)} -} - -func (_c *mockSendOnlyClient_ChainID_Call[CHAIN_ID]) Run(run func(_a0 context.Context)) *mockSendOnlyClient_ChainID_Call[CHAIN_ID] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockSendOnlyClient_ChainID_Call[CHAIN_ID]) Return(_a0 CHAIN_ID, _a1 error) *mockSendOnlyClient_ChainID_Call[CHAIN_ID] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockSendOnlyClient_ChainID_Call[CHAIN_ID]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockSendOnlyClient_ChainID_Call[CHAIN_ID] { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with no fields -func (_m *mockSendOnlyClient[CHAIN_ID]) Close() { - _m.Called() -} - -// mockSendOnlyClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockSendOnlyClient_Close_Call[CHAIN_ID types.ID] struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) Close() *mockSendOnlyClient_Close_Call[CHAIN_ID] { - return &mockSendOnlyClient_Close_Call[CHAIN_ID]{Call: _e.mock.On("Close")} -} - -func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) Run(run func()) *mockSendOnlyClient_Close_Call[CHAIN_ID] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) Return() *mockSendOnlyClient_Close_Call[CHAIN_ID] { - _c.Call.Return() - return _c -} - -func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) RunAndReturn(run func()) *mockSendOnlyClient_Close_Call[CHAIN_ID] { - _c.Run(run) - return _c -} - -// Dial provides a mock function with given fields: ctx -func (_m *mockSendOnlyClient[CHAIN_ID]) Dial(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockSendOnlyClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' -type mockSendOnlyClient_Dial_Call[CHAIN_ID types.ID] struct { - *mock.Call -} - -// Dial is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) Dial(ctx interface{}) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { - return &mockSendOnlyClient_Dial_Call[CHAIN_ID]{Call: _e.mock.On("Dial", ctx)} -} - -func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) Run(run func(ctx context.Context)) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) Return(_a0 error) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) RunAndReturn(run func(context.Context) error) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { - _c.Call.Return(run) - return _c -} - -// 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 deleted file mode 100644 index 16d463df3de..00000000000 --- a/common/client/mock_send_only_node_test.go +++ /dev/null @@ -1,357 +0,0 @@ -// Code generated by mockery v2.50.0. 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 interface{}] struct { - mock.Mock -} - -type mockSendOnlyNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { - mock *mock.Mock -} - -func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) EXPECT() *mockSendOnlyNode_Expecter[CHAIN_ID, RPC] { - return &mockSendOnlyNode_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockSendOnlyNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockSendOnlyNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) Close() *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC] { - return &mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Close")} -} - -func (_c *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC]) RunAndReturn(run func() error) *mockSendOnlyNode_Close_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// ConfiguredChainID provides a mock function with no fields -func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ConfiguredChainID") - } - - var r0 CHAIN_ID - if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(CHAIN_ID) - } - } - - return r0 -} - -// mockSendOnlyNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID' -type mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// ConfiguredChainID is a helper method to define mock.On call -func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) ConfiguredChainID() *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { - return &mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]{Call: _e.mock.On("ConfiguredChainID")} -} - -func (_c *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Return(_a0 CHAIN_ID) *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) RunAndReturn(run func() CHAIN_ID) *mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// Name provides a mock function with no fields -func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Name") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// mockSendOnlyNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' -type mockSendOnlyNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Name is a helper method to define mock.On call -func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) Name() *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] { - return &mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")} -} - -func (_c *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// RPC provides a mock function with no fields -func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for RPC") - } - - var r0 RPC - if rf, ok := ret.Get(0).(func() RPC); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(RPC) - } - } - - return r0 -} - -// mockSendOnlyNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC' -type mockSendOnlyNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// RPC is a helper method to define mock.On call -func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) RPC() *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC] { - return &mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC]{Call: _e.mock.On("RPC")} -} - -func (_c *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC]) Return(_a0 RPC) *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC]) RunAndReturn(run func() RPC) *mockSendOnlyNode_RPC_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// Start provides a mock function with given fields: _a0 -func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for Start") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockSendOnlyNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type mockSendOnlyNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// Start is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) Start(_a0 interface{}) *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC] { - return &mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Start", _a0)} -} - -func (_c *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]) Run(run func(_a0 context.Context)) *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC]) RunAndReturn(run func(context.Context) error) *mockSendOnlyNode_Start_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// State provides a mock function with no fields -func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for State") - } - - var r0 nodeState - if rf, ok := ret.Get(0).(func() nodeState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(nodeState) - } - - return r0 -} - -// mockSendOnlyNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type mockSendOnlyNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// State is a helper method to define mock.On call -func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) State() *mockSendOnlyNode_State_Call[CHAIN_ID, RPC] { - return &mockSendOnlyNode_State_Call[CHAIN_ID, RPC]{Call: _e.mock.On("State")} -} - -func (_c *mockSendOnlyNode_State_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_State_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockSendOnlyNode_State_Call[CHAIN_ID, RPC]) Return(_a0 nodeState) *mockSendOnlyNode_State_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockSendOnlyNode_State_Call[CHAIN_ID, RPC]) RunAndReturn(run func() nodeState) *mockSendOnlyNode_State_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// String provides a mock function with no fields -func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) String() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for String") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// mockSendOnlyNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' -type mockSendOnlyNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct { - *mock.Call -} - -// String is a helper method to define mock.On call -func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) String() *mockSendOnlyNode_String_Call[CHAIN_ID, RPC] { - return &mockSendOnlyNode_String_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")} -} - -func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_String_Call[CHAIN_ID, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockSendOnlyNode_String_Call[CHAIN_ID, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockSendOnlyNode_String_Call[CHAIN_ID, RPC] { - _c.Call.Return(run) - return _c -} - -// 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 interface{}](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/mocks/config.go b/common/client/mocks/config.go deleted file mode 100644 index 95b57cce0c3..00000000000 --- a/common/client/mocks/config.go +++ /dev/null @@ -1,31 +0,0 @@ -package mocks - -import "time" - -type ChainConfig struct { - IsFinalityTagEnabled bool - FinalityDepthVal uint32 - NoNewHeadsThresholdVal time.Duration - FinalizedBlockOffsetVal uint32 - NoNewFinalizedHeadsThresholdVal time.Duration -} - -func (t ChainConfig) NodeNoNewHeadsThreshold() time.Duration { - return t.NoNewHeadsThresholdVal -} - -func (t ChainConfig) FinalityDepth() uint32 { - return t.FinalityDepthVal -} - -func (t ChainConfig) FinalityTagEnabled() bool { - return t.IsFinalityTagEnabled -} - -func (t ChainConfig) FinalizedBlockOffset() uint32 { - return t.FinalizedBlockOffsetVal -} - -func (t ChainConfig) NoNewFinalizedHeadsThreshold() time.Duration { - return t.NoNewFinalizedHeadsThresholdVal -} diff --git a/common/client/models.go b/common/client/models.go deleted file mode 100644 index 526bb25c887..00000000000 --- a/common/client/models.go +++ /dev/null @@ -1,121 +0,0 @@ -package client - -import ( - "bytes" - "fmt" -) - -type SendTxReturnCode int - -// SendTxReturnCode is a generalized client error that dictates what should be the next action, depending on the RPC error response. -const ( - Successful SendTxReturnCode = iota + 1 - Fatal // Unrecoverable error. Most likely the attempt should be thrown away. - Retryable // The error returned by the RPC indicates that if we retry with the same attempt, the tx will eventually go through. - Underpriced // Attempt was underpriced. New estimation is needed with bumped gas price. - Unknown // Tx failed with an error response that is not recognized by the client. - Unsupported // Attempt failed with an error response that is not supported by the client for the given chain. - TransactionAlreadyKnown // The transaction that was sent has already been received by the RPC. - InsufficientFunds // Tx was rejected due to insufficient funds. - 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. - TerminallyStuck // The error returned when a transaction is or could get terminally stuck in the mempool without any chance of inclusion. - sendTxReturnCodeLen // tracks the number of errors. Must always be last -) - -// sendTxSevereErrors - error codes which signal that transaction would never be accepted in its current form by the node -var sendTxSevereErrors = []SendTxReturnCode{Fatal, Underpriced, Unsupported, ExceedsMaxFee, FeeOutOfValidRange, Unknown} - -// sendTxSuccessfulCodes - error codes which signal that transaction was accepted by the node -var sendTxSuccessfulCodes = []SendTxReturnCode{Successful, TransactionAlreadyKnown} - -func (c SendTxReturnCode) String() string { - switch c { - case Successful: - return "Successful" - case Fatal: - return "Fatal" - case Retryable: - return "Retryable" - case Underpriced: - return "Underpriced" - case Unknown: - return "Unknown" - case Unsupported: - return "Unsupported" - case TransactionAlreadyKnown: - return "TransactionAlreadyKnown" - case InsufficientFunds: - return "InsufficientFunds" - case ExceedsMaxFee: - return "ExceedsMaxFee" - case FeeOutOfValidRange: - return "FeeOutOfValidRange" - case TerminallyStuck: - return "TerminallyStuck" - default: - return fmt.Sprintf("SendTxReturnCode(%d)", c) - } -} - -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) - } -} - -// syncStatus - defines problems related to RPC's state synchronization. Can be used as a bitmask to define multiple issues -type syncStatus int - -const ( - // syncStatusSynced - RPC is fully synced - syncStatusSynced = 0 - // syncStatusNotInSyncWithPool - RPC is lagging behind the highest block observed within the pool of RPCs - syncStatusNotInSyncWithPool syncStatus = 1 << iota - // syncStatusNoNewHead - RPC failed to produce a new head for too long - syncStatusNoNewHead - // syncStatusNoNewFinalizedHead - RPC failed to produce a new finalized head for too long - syncStatusNoNewFinalizedHead - syncStatusLen -) - -func (s syncStatus) String() string { - if s == syncStatusSynced { - return "Synced" - } - var result bytes.Buffer - for i := syncStatusNotInSyncWithPool; i < syncStatusLen; i = i << 1 { - if i&s == 0 { - continue - } - result.WriteString(i.string()) - result.WriteString(",") - } - result.Truncate(result.Len() - 1) - return result.String() -} - -func (s syncStatus) string() string { - switch s { - case syncStatusNotInSyncWithPool: - return "NotInSyncWithRPCPool" - case syncStatusNoNewHead: - return "NoNewHead" - case syncStatusNoNewFinalizedHead: - return "NoNewFinalizedHead" - default: - return fmt.Sprintf("syncStatus(%d)", s) - } -} diff --git a/common/client/models_test.go b/common/client/models_test.go deleted file mode 100644 index a10592c3b68..00000000000 --- a/common/client/models_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package client - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSendTxReturnCode_String(t *testing.T) { - // ensure all the SendTxReturnCodes have proper name - for c := 1; c < int(sendTxReturnCodeLen); c++ { - strC := SendTxReturnCode(c).String() - if strings.Contains(strC, "SendTxReturnCode(") { - t.Errorf("Expected %s to have a proper string representation", strC) - } - } -} - -func TestSyncStatus_String(t *testing.T) { - t.Run("All of the statuses have proper string representation", func(t *testing.T) { - for i := syncStatusNotInSyncWithPool; i < syncStatusLen; i <<= 1 { - // ensure that i's string representation is not equal to `syncStatus(%d)` - assert.NotContains(t, i.String(), "syncStatus(") - } - }) - t.Run("Unwraps mask", func(t *testing.T) { - testCases := []struct { - Mask syncStatus - ExpectedStr string - }{ - { - ExpectedStr: "Synced", - }, - { - Mask: syncStatusNotInSyncWithPool | syncStatusNoNewHead, - ExpectedStr: "NotInSyncWithRPCPool,NoNewHead", - }, - { - Mask: syncStatusNotInSyncWithPool | syncStatusNoNewHead | syncStatusNoNewFinalizedHead, - ExpectedStr: "NotInSyncWithRPCPool,NoNewHead,NoNewFinalizedHead", - }, - } - for _, testCase := range testCases { - t.Run(testCase.ExpectedStr, func(t *testing.T) { - assert.Equal(t, testCase.ExpectedStr, testCase.Mask.String()) - }) - } - }) -} diff --git a/common/client/multi_node.go b/common/client/multi_node.go deleted file mode 100644 index b946fb8fc2a..00000000000 --- a/common/client/multi_node.go +++ /dev/null @@ -1,364 +0,0 @@ -package client - -import ( - "context" - "fmt" - "math/big" - "sync" - "time" - - "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" -) - -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") -) - -// 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, - RPC any, -] struct { - services.Service - eng *services.Engine - - primaryNodes []Node[CHAIN_ID, RPC] - sendOnlyNodes []SendOnlyNode[CHAIN_ID, RPC] - chainID CHAIN_ID - lggr logger.SugaredLogger - selectionMode string - nodeSelector NodeSelector[CHAIN_ID, RPC] - leaseDuration time.Duration - leaseTicker *time.Ticker - chainFamily string - reportInterval time.Duration - deathDeclarationDelay time.Duration - - activeMu sync.RWMutex - activeNode Node[CHAIN_ID, RPC] -} - -func NewMultiNode[ - CHAIN_ID types.ID, - RPC any, -]( - lggr logger.Logger, - selectionMode string, // type of the "best" RPC selector (e.g HighestHead, RoundRobin, etc.) - leaseDuration time.Duration, // defines interval on which new "best" RPC should be selected - primaryNodes []Node[CHAIN_ID, RPC], - sendOnlyNodes []SendOnlyNode[CHAIN_ID, RPC], - chainID CHAIN_ID, // configured chain ID (used to verify that passed primaryNodes belong to the same chain) - chainFamily string, // name of the chain family - used in the metrics - deathDeclarationDelay time.Duration, -) *MultiNode[CHAIN_ID, RPC] { - nodeSelector := newNodeSelector(selectionMode, primaryNodes) - // 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, RPC]{ - primaryNodes: primaryNodes, - sendOnlyNodes: sendOnlyNodes, - chainID: chainID, - selectionMode: selectionMode, - nodeSelector: nodeSelector, - leaseDuration: leaseDuration, - chainFamily: chainFamily, - reportInterval: reportInterval, - deathDeclarationDelay: deathDeclarationDelay, - } - c.Service, c.eng = services.Config{ - Name: "MultiNode", - Start: c.start, - Close: c.close, - }.NewServiceEngine(logger.With(lggr, "chainID", chainID.String())) - c.lggr = c.eng.SugaredLogger - - c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) - - return c -} - -func (c *MultiNode[CHAIN_ID, RPC]) ChainID() CHAIN_ID { - return c.chainID -} - -func (c *MultiNode[CHAIN_ID, RPC]) DoAll(ctx context.Context, do func(ctx context.Context, rpc RPC, isSendOnly bool)) error { - return c.eng.IfNotStopped(func() error { - callsCompleted := 0 - for _, n := range c.primaryNodes { - select { - case <-ctx.Done(): - return ctx.Err() - default: - if n.State() != nodeStateAlive { - continue - } - do(ctx, n.RPC(), false) - callsCompleted++ - } - } - - for _, n := range c.sendOnlyNodes { - select { - case <-ctx.Done(): - return ctx.Err() - default: - if n.State() != nodeStateAlive { - continue - } - do(ctx, n.RPC(), true) - } - } - if callsCompleted == 0 { - return ErroringNodeError - } - return nil - }) -} - -func (c *MultiNode[CHAIN_ID, RPC]) NodeStates() map[string]string { - states := map[string]string{} - for _, n := range c.primaryNodes { - states[n.Name()] = n.State().String() - } - for _, n := range c.sendOnlyNodes { - states[n.Name()] = n.State().String() - } - return states -} - -// Start 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, RPC]) start(ctx context.Context) error { - if len(c.primaryNodes) == 0 { - return fmt.Errorf("no available nodes for chain %s", c.chainID.String()) - } - var ms services.MultiStart - for _, n := range c.primaryNodes { - if n.ConfiguredChainID().String() != c.chainID.String() { - return ms.CloseBecause(fmt.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())) - } - n.SetPoolChainInfoProvider(c) - // node will handle its own redialing and automatic recovery - if err := ms.Start(ctx, n); err != nil { - return err - } - } - for _, s := range c.sendOnlyNodes { - if s.ConfiguredChainID().String() != c.chainID.String() { - return ms.CloseBecause(fmt.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.eng.Go(c.runLoop) - - if c.leaseDuration.Seconds() > 0 && c.selectionMode != NodeSelectionModeRoundRobin { - c.lggr.Infof("The MultiNode will switch to best node every %s", c.leaseDuration.String()) - c.eng.Go(c.checkLeaseLoop) - } else { - c.lggr.Info("Best node switching is disabled") - } - - return nil -} - -// Close tears down the MultiNode and closes all nodes -func (c *MultiNode[CHAIN_ID, RPC]) close() error { - return services.CloseAll(services.MultiCloser(c.primaryNodes), services.MultiCloser(c.sendOnlyNodes)) -} - -// SelectRPC 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, RPC]) SelectRPC() (rpc RPC, 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, RPC]) selectNode() (node Node[CHAIN_ID, RPC], 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 - } - - var prevNodeName string - if c.activeNode != nil { - prevNodeName = c.activeNode.String() - c.activeNode.UnsubscribeAllExceptAliveLoop() - } - c.activeNode = c.nodeSelector.Select() - if c.activeNode == nil { - c.lggr.Criticalw("No live RPC nodes available", "NodeSelectionMode", c.nodeSelector.Name()) - c.eng.EmitHealthErr(fmt.Errorf("no live nodes available for chain %s", c.chainID.String())) - return nil, ErroringNodeError - } - - c.lggr.Debugw("Switched to a new active node due to prev node heath issues", "prevNode", prevNodeName, "newNode", c.activeNode.String()) - return c.activeNode, err -} - -// LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being marked as out-of-sync. -// Return highest ChainInfo most recently received by the alive nodes. -// E.g. If Node A's the most recent block is 10 and highest 15 and for Node B it's - 12 and 14. This method will return 12. -func (c *MultiNode[CHAIN_ID, RPC]) LatestChainInfo() (int, ChainInfo) { - var nLiveNodes int - ch := ChainInfo{ - TotalDifficulty: big.NewInt(0), - } - for _, n := range c.primaryNodes { - if s, nodeChainInfo := n.StateAndLatest(); s == nodeStateAlive { - nLiveNodes++ - ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) - ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) - ch.TotalDifficulty = MaxTotalDifficulty(ch.TotalDifficulty, nodeChainInfo.TotalDifficulty) - } - } - return nLiveNodes, ch -} - -// HighestUserObservations - returns highest ChainInfo ever observed by any user of the MultiNode -func (c *MultiNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo { - ch := ChainInfo{ - TotalDifficulty: big.NewInt(0), - } - for _, n := range c.primaryNodes { - nodeChainInfo := n.HighestUserObservations() - ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) - ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) - ch.TotalDifficulty = MaxTotalDifficulty(ch.TotalDifficulty, nodeChainInfo.TotalDifficulty) - } - return ch -} - -func (c *MultiNode[CHAIN_ID, RPC]) checkLease() { - bestNode := c.nodeSelector.Select() - for _, n := range c.primaryNodes { - // 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 { - c.lggr.Infof("Switching to best node from %q to %q", n.String(), bestNode.String()) - n.UnsubscribeAllExceptAliveLoop() - } - } - - c.activeMu.Lock() - defer c.activeMu.Unlock() - if bestNode != c.activeNode { - if c.activeNode != nil { - c.activeNode.UnsubscribeAllExceptAliveLoop() - } - c.activeNode = bestNode - } -} - -func (c *MultiNode[CHAIN_ID, RPC]) checkLeaseLoop(ctx context.Context) { - c.leaseTicker = time.NewTicker(c.leaseDuration) - defer c.leaseTicker.Stop() - - for { - select { - case <-c.leaseTicker.C: - c.checkLease() - case <-ctx.Done(): - return - } - } -} - -func (c *MultiNode[CHAIN_ID, RPC]) runLoop(ctx context.Context) { - nodeStates := make([]nodeWithState, len(c.primaryNodes)) - for i, n := range c.primaryNodes { - nodeStates[i] = nodeWithState{ - Node: n.String(), - State: n.State().String(), - DeadSince: nil, - } - } - - c.report(nodeStates) - - monitor := services.NewTicker(c.reportInterval) - defer monitor.Stop() - - for { - select { - case <-monitor.C: - c.report(nodeStates) - case <-ctx.Done(): - return - } - } -} - -type nodeWithState struct { - Node string - State string - DeadSince *time.Time -} - -func (c *MultiNode[CHAIN_ID, RPC]) report(nodesStateInfo []nodeWithState) { - start := time.Now() - var dead int - counts := make(map[nodeState]int) - for i, n := range c.primaryNodes { - state := n.State() - counts[state]++ - nodesStateInfo[i].State = state.String() - if state == nodeStateAlive { - nodesStateInfo[i].DeadSince = nil - continue - } - - if nodesStateInfo[i].DeadSince == nil { - nodesStateInfo[i].DeadSince = &start - } - - if start.Sub(*nodesStateInfo[i].DeadSince) >= c.deathDeclarationDelay { - dead++ - } - } - for _, state := range allNodeStates { - count := counts[state] - PromMultiNodeRPCNodeStates.WithLabelValues(c.chainFamily, c.chainID.String(), state.String()).Set(float64(count)) - } - - total := len(c.primaryNodes) - live := total - dead - c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) - if total == dead { - rerr := fmt.Errorf("no primary nodes available: 0/%d nodes are alive", total) - c.lggr.Criticalw(rerr.Error(), "nodeStates", nodesStateInfo) - c.eng.EmitHealthErr(rerr) - } else if dead > 0 { - c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) - } -} diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go deleted file mode 100644 index c1636881dd3..00000000000 --- a/common/client/multi_node_test.go +++ /dev/null @@ -1,517 +0,0 @@ -package client - -import ( - "fmt" - "math/big" - "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-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -type multiNodeRPCClient RPCClient[types.ID, types.Head[Hashable]] - -type testMultiNode struct { - *MultiNode[types.ID, multiNodeRPCClient] -} - -type multiNodeOpts struct { - logger logger.Logger - selectionMode string - leaseDuration time.Duration - nodes []Node[types.ID, multiNodeRPCClient] - sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient] - chainID types.ID - chainFamily string - deathDeclarationDelay time.Duration -} - -func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { - if opts.logger == nil { - opts.logger = logger.Test(t) - } - - result := NewMultiNode[types.ID, multiNodeRPCClient]( - opts.logger, opts.selectionMode, opts.leaseDuration, opts.nodes, opts.sendonlys, opts.chainID, opts.chainFamily, opts.deathDeclarationDelay) - return testMultiNode{ - result, - } -} - -func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, multiNodeRPCClient] { - return newNodeWithState(t, chainID, nodeStateAlive) -} - -func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode[types.ID, multiNodeRPCClient] { - node := newMockNode[types.ID, multiNodeRPCClient](t) - node.On("ConfiguredChainID").Return(chainID).Once() - node.On("Start", mock.Anything).Return(nil).Once() - node.On("Close").Return(nil).Once() - node.On("String").Return(fmt.Sprintf("healthy_node_%d", rand.Int())).Maybe() - node.On("SetPoolChainInfoProvider", mock.Anything).Once() - node.On("State").Return(state).Maybe() - return node -} - -func TestMultiNode_Dial(t *testing.T) { - t.Parallel() - - newMockNode := newMockNode[types.ID, 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.Start(tests.Context(t)) - assert.ErrorContains(t, err, fmt.Sprintf("no available nodes for chain %s", mn.chainID)) - }) - 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, multiNodeRPCClient]{node}, - }) - err := mn.Start(tests.Context(t)) - assert.ErrorContains(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() - node.On("SetPoolChainInfoProvider", mock.Anything).Once() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, multiNodeRPCClient]{node}, - }) - err := mn.Start(tests.Context(t)) - assert.ErrorIs(t, err, expectedError) - }) - - 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() - node2.On("SetPoolChainInfoProvider", mock.Anything).Once() - - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, - }) - err := mn.Start(tests.Context(t)) - assert.ErrorIs(t, err, expectedError) - }) - 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, multiNodeRPCClient]{node}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly}, - }) - err := mn.Start(tests.Context(t)) - assert.ErrorContains(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, multiNodeRPCClient]{node}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly1, sendOnly2}, - }) - err := mn.Start(tests.Context(t)) - assert.ErrorIs(t, err, expectedError) - }) - 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, multiNodeRPCClient]{node}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newHealthySendOnly(t, chainID)}, - }) - servicetest.Run(t, mn) - 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.TestObserved(t, zap.WarnLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, - logger: lggr, - }) - mn.reportInterval = tests.TestInterval - mn.deathDeclarationDelay = tests.TestInterval - servicetest.Run(t, mn) - 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.TestObserved(t, zap.WarnLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, multiNodeRPCClient]{node}, - logger: lggr, - }) - mn.reportInterval = tests.TestInterval - mn.deathDeclarationDelay = tests.TestInterval - servicetest.Run(t, mn) - tests.AssertLogCountEventually(t, observedLogs, "no primary nodes available: 0/1 nodes are alive", 2) - err := mn.HealthReport()["MultiNode"] - 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.TestObserved(t, zap.InfoLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - logger: lggr, - nodes: []Node[types.ID, multiNodeRPCClient]{node}, - }) - servicetest.Run(t, mn) - 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.TestObserved(t, zap.InfoLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeHighestHead, - chainID: chainID, - logger: lggr, - nodes: []Node[types.ID, multiNodeRPCClient]{node}, - leaseDuration: 0, - }) - servicetest.Run(t, mn) - 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("UnsubscribeAllExceptAliveLoop") - bestNode := newHealthyNode(t, chainID) - nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) - nodeSelector.On("Select").Return(bestNode) - lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeHighestHead, - chainID: chainID, - logger: lggr, - nodes: []Node[types.ID, multiNodeRPCClient]{node, bestNode}, - leaseDuration: tests.TestInterval, - }) - mn.nodeSelector = nodeSelector - servicetest.Run(t, mn) - 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, multiNodeRPCClient](t) - node.On("State").Return(state).Once() - node.On("Name").Return(name).Once() - opts.nodes = append(opts.nodes, node) - - sendOnly := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) - sendOnlyName := "send_only_" + name - sendOnly.On("State").Return(state).Once() - sendOnly.On("Name").Return(sendOnlyName).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, multiNodeRPCClient](t) - node1.On("State").Return(nodeStateAlive).Once() - node1.On("String").Return("node1").Maybe() - node2 := newMockNode[types.ID, multiNodeRPCClient](t) - node2.On("String").Return("node2").Maybe() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, - }) - nodeSelector := newMockNodeSelector[types.ID, 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, multiNodeRPCClient](t) - oldBest.On("String").Return("oldBest").Maybe() - oldBest.On("UnsubscribeAllExceptAliveLoop") - newBest := newMockNode[types.ID, multiNodeRPCClient](t) - newBest.On("String").Return("newBest").Maybe() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, multiNodeRPCClient]{oldBest, newBest}, - }) - nodeSelector := newMockNodeSelector[types.ID, 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.TestObserved(t, zap.InfoLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - logger: lggr, - }) - nodeSelector := newMockNodeSelector[types.ID, 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_ChainInfo(t *testing.T) { - t.Parallel() - type nodeParams struct { - LatestChainInfo ChainInfo - HighestUserObservations ChainInfo - State nodeState - } - testCases := []struct { - Name string - ExpectedNLiveNodes int - ExpectedLatestChainInfo ChainInfo - ExpectedHighestUserObservations ChainInfo - NodeParams []nodeParams - }{ - { - Name: "no nodes", - ExpectedLatestChainInfo: ChainInfo{ - TotalDifficulty: big.NewInt(0), - }, - ExpectedHighestUserObservations: ChainInfo{ - TotalDifficulty: big.NewInt(0), - }, - }, - { - Name: "Best node is not healthy", - ExpectedNLiveNodes: 3, - ExpectedLatestChainInfo: ChainInfo{ - BlockNumber: 20, - FinalizedBlockNumber: 10, - TotalDifficulty: big.NewInt(10), - }, - ExpectedHighestUserObservations: ChainInfo{ - BlockNumber: 1005, - FinalizedBlockNumber: 995, - TotalDifficulty: big.NewInt(2005), - }, - NodeParams: []nodeParams{ - { - State: nodeStateOutOfSync, - LatestChainInfo: ChainInfo{ - BlockNumber: 1000, - FinalizedBlockNumber: 990, - TotalDifficulty: big.NewInt(2000), - }, - HighestUserObservations: ChainInfo{ - BlockNumber: 1005, - FinalizedBlockNumber: 995, - TotalDifficulty: big.NewInt(2005), - }, - }, - { - State: nodeStateAlive, - LatestChainInfo: ChainInfo{ - BlockNumber: 20, - FinalizedBlockNumber: 10, - TotalDifficulty: big.NewInt(9), - }, - HighestUserObservations: ChainInfo{ - BlockNumber: 25, - FinalizedBlockNumber: 15, - TotalDifficulty: big.NewInt(14), - }, - }, - { - State: nodeStateAlive, - LatestChainInfo: ChainInfo{ - BlockNumber: 19, - FinalizedBlockNumber: 9, - TotalDifficulty: big.NewInt(10), - }, - HighestUserObservations: ChainInfo{ - BlockNumber: 24, - FinalizedBlockNumber: 14, - TotalDifficulty: big.NewInt(15), - }, - }, - { - State: nodeStateAlive, - LatestChainInfo: ChainInfo{ - BlockNumber: 11, - FinalizedBlockNumber: 1, - TotalDifficulty: nil, - }, - HighestUserObservations: ChainInfo{ - BlockNumber: 16, - FinalizedBlockNumber: 6, - 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, multiNodeRPCClient](t) - mn.primaryNodes = append(mn.primaryNodes, node) - node.On("StateAndLatest").Return(params.State, params.LatestChainInfo) - node.On("HighestUserObservations").Return(params.HighestUserObservations) - } - - nNodes, latestChainInfo := mn.LatestChainInfo() - assert.Equal(t, tc.ExpectedNLiveNodes, nNodes) - assert.Equal(t, tc.ExpectedLatestChainInfo, latestChainInfo) - - highestChainInfo := mn.HighestUserObservations() - assert.Equal(t, tc.ExpectedHighestUserObservations, highestChainInfo) - }) - } -} diff --git a/common/client/node.go b/common/client/node.go deleted file mode 100644 index 66161ac5d5f..00000000000 --- a/common/client/node.go +++ /dev/null @@ -1,336 +0,0 @@ -package client - -import ( - "context" - "errors" - "fmt" - "net/url" - "sync" - "time" - - "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" -) - -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 - NodeIsSyncingEnabled() bool - FinalizedBlockPollInterval() time.Duration - EnforceRepeatableRead() bool - DeathDeclarationDelay() time.Duration - NewHeadsPollInterval() time.Duration -} - -type ChainConfig interface { - NodeNoNewHeadsThreshold() time.Duration - NoNewFinalizedHeadsThreshold() time.Duration - FinalityDepth() uint32 - FinalityTagEnabled() bool - FinalizedBlockOffset() uint32 -} - -type Node[ - CHAIN_ID types.ID, - RPC any, -] interface { - // State returns most accurate state of the Node on the moment of call. - // While some of the checks may be performed in the background and State may return cached value, critical, like - // `FinalizedBlockOutOfSync`, must be executed upon every call. - State() nodeState - // StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle. - StateAndLatest() (nodeState, ChainInfo) - // HighestUserObservations - returns highest ChainInfo ever observed by underlying RPC excluding results of health check requests - HighestUserObservations() ChainInfo - SetPoolChainInfoProvider(PoolChainInfoProvider) - // Name is a unique identifier for this node. - Name() string - // String - returns string representation of the node, useful for debugging (name + URLS used to connect to the RPC) - String() string - RPC() RPC - // UnsubscribeAllExceptAliveLoop - closes all subscriptions except the aliveLoop subscription - UnsubscribeAllExceptAliveLoop() - ConfiguredChainID() CHAIN_ID - // Order - returns priority order configured for the RPC - Order() int32 - // Start - starts health checks - Start(context.Context) error - Close() error -} - -type node[ - CHAIN_ID types.ID, - HEAD Head, - RPC RPCClient[CHAIN_ID, HEAD], -] struct { - services.StateMachine - lfcLog logger.Logger - name string - id int - chainID CHAIN_ID - nodePoolCfg NodeConfig - chainCfg ChainConfig - order int32 - chainFamily string - - ws *url.URL - http *url.URL - - rpc RPC - - stateMu sync.RWMutex // protects state* fields - state nodeState - - poolInfoProvider PoolChainInfoProvider - - stopCh services.StopChan - // wg waits for subsidiary goroutines - wg sync.WaitGroup - - healthCheckSubs []types.Subscription -} - -func NewNode[ - CHAIN_ID types.ID, - HEAD Head, - RPC RPCClient[CHAIN_ID, HEAD], -]( - nodeCfg NodeConfig, - chainCfg ChainConfig, - lggr logger.Logger, - wsuri *url.URL, - httpuri *url.URL, - name string, - id int, - chainID CHAIN_ID, - nodeOrder int32, - rpc RPC, - chainFamily string, -) Node[CHAIN_ID, RPC] { - n := new(node[CHAIN_ID, HEAD, RPC]) - n.name = name - n.id = id - n.chainID = chainID - n.nodePoolCfg = nodeCfg - n.chainCfg = chainCfg - n.order = nodeOrder - if wsuri != nil { - n.ws = wsuri - } - if httpuri != nil { - n.http = httpuri - } - n.stopCh = make(services.StopChan) - lggr = logger.Named(lggr, "Node") - lggr = logger.With(lggr, - "nodeTier", Primary.String(), - "nodeName", name, - "node", n.String(), - "chainID", chainID, - "nodeOrder", n.order, - ) - n.lfcLog = logger.Named(lggr, "Lifecycle") - n.rpc = rpc - n.chainFamily = chainFamily - return n -} - -func (n *node[CHAIN_ID, HEAD, RPC]) String() string { - s := fmt.Sprintf("(%s)%s", Primary.String(), n.name) - if n.ws != nil { - s = s + fmt.Sprintf(":%s", 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 -} - -// unsubscribeAllExceptAliveLoop is not thread-safe; it should only be called -// while holding the stateMu lock. -func (n *node[CHAIN_ID, HEAD, RPC]) unsubscribeAllExceptAliveLoop() { - n.rpc.UnsubscribeAllExcept(n.healthCheckSubs...) -} - -func (n *node[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() { - n.stateMu.Lock() - defer n.stateMu.Unlock() - n.unsubscribeAllExceptAliveLoop() -} - -func (n *node[CHAIN_ID, HEAD, RPC]) Close() error { - return n.StopOnce(n.name, n.close) -} - -func (n *node[CHAIN_ID, HEAD, RPC]) close() error { - defer func() { - n.wg.Wait() - n.rpc.Close() - }() - - n.stateMu.Lock() - defer n.stateMu.Unlock() - - close(n.stopCh) - 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) - - state := n.verifyConn(startCtx, n.lfcLog) - n.declareState(state) -} - -// verifyChainID checks that connection to the node matches the given chain ID -// Not thread-safe -// Pure verifyChainID: does not mutate node "state" field. -func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lggr logger.Logger) nodeState { - promPoolRPCNodeVerifies.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() - promFailed := func() { - promPoolRPCNodeVerifiesFailed.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() - } - - st := n.getCachedState() - switch st { - case nodeStateClosed: - // The node is already closed, and any subsequent transition is invalid. - // To make spotting such transitions a bit easier, return the invalid node state. - return nodeStateLen - case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing: - default: - panic(fmt.Sprintf("cannot verify node in state %v", st)) - } - - var chainID CHAIN_ID - var err error - if chainID, err = n.rpc.ChainID(callerCtx); err != nil { - promFailed() - lggr.Errorw("Failed to verify chain ID for node", "err", err, "nodeState", n.getCachedState()) - return nodeStateUnreachable - } else if chainID.String() != n.chainID.String() { - promFailed() - err = fmt.Errorf( - "rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s: %w", - chainID.String(), - n.chainID.String(), - n.name, - errInvalidChainID, - ) - lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err, "nodeState", n.getCachedState()) - return nodeStateInvalidChainID - } - - promPoolRPCNodeVerifiesSuccess.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() - - return nodeStateAlive -} - -// createVerifiedConn - establishes new connection with the RPC and verifies that it's valid: chainID matches, and it's not syncing. -// Returns desired state if one of the verifications fails. Otherwise, returns nodeStateAlive. -func (n *node[CHAIN_ID, HEAD, RPC]) createVerifiedConn(ctx context.Context, lggr logger.Logger) nodeState { - if err := n.rpc.Dial(ctx); err != nil { - n.lfcLog.Errorw("Dial failed: Node is unreachable", "err", err, "nodeState", n.getCachedState()) - return nodeStateUnreachable - } - - return n.verifyConn(ctx, lggr) -} - -// verifyConn - verifies that current connection is valid: chainID matches, and it's not syncing. -// Returns desired state if one of the verifications fails. Otherwise, returns nodeStateAlive. -func (n *node[CHAIN_ID, HEAD, RPC]) verifyConn(ctx context.Context, lggr logger.Logger) nodeState { - state := n.verifyChainID(ctx, lggr) - if state != nodeStateAlive { - return state - } - - if n.nodePoolCfg.NodeIsSyncingEnabled() { - isSyncing, err := n.rpc.IsSyncing(ctx) - if err != nil { - lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.getCachedState()) - return nodeStateUnreachable - } - - if isSyncing { - lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.getCachedState()) - return nodeStateSyncing - } - } - - return nodeStateAlive -} - -func (n *node[CHAIN_ID, HEAD, RPC]) Order() int32 { - return n.order -} - -func (n *node[CHAIN_ID, HEAD, RPC]) newCtx() (context.Context, context.CancelFunc) { - ctx, cancel := n.stopCh.NewCtx() - ctx = CtxAddHealthCheckFlag(ctx) - return ctx, cancel -} diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go deleted file mode 100644 index b707e9f4375..00000000000 --- a/common/client/node_fsm.go +++ /dev/null @@ -1,377 +0,0 @@ -package client - -import ( - "fmt" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -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"}) - promPoolRPCNodeTransitionsToSyncing = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "pool_rpc_node_num_transitions_to_syncing", - Help: transitionString(nodeStateSyncing), - }, []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" - case nodeStateSyncing: - return "Syncing" - case nodeStateFinalizedBlockOutOfSync: - return "FinalizedBlockOutOfSync" - 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 - // nodeStateSyncing is a node that is actively back-filling blockchain. Usually, it's a newly set up node that is - // still syncing the chain. The main difference from `nodeStateOutOfSync` is that it represents state relative - // to other primary nodes configured in the MultiNode. In contrast, `nodeStateSyncing` represents the internal state of - // the node (RPC). - nodeStateSyncing - // nodeStateFinalizedBlockOutOfSync - node is lagging behind on latest finalized block - nodeStateFinalizedBlockOutOfSync - // 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.recalculateState() -} - -func (n *node[CHAIN_ID, HEAD, RPC]) getCachedState() nodeState { - n.stateMu.RLock() - defer n.stateMu.RUnlock() - return n.state -} - -func (n *node[CHAIN_ID, HEAD, RPC]) recalculateState() nodeState { - if n.state != nodeStateAlive { - return n.state - } - - // double check that node is not lagging on finalized block - if n.nodePoolCfg.EnforceRepeatableRead() && n.isFinalizedBlockOutOfSync() { - return nodeStateFinalizedBlockOutOfSync - } - - return nodeStateAlive -} - -func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedBlockOutOfSync() bool { - if n.poolInfoProvider == nil { - return false - } - - highestObservedByCaller := n.poolInfoProvider.HighestUserObservations() - latest, rpcHighest := n.rpc.GetInterceptedChainInfo() - isOutOfSync := false - if n.chainCfg.FinalityTagEnabled() { - isOutOfSync = latest.FinalizedBlockNumber < highestObservedByCaller.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) - } else { - isOutOfSync = latest.BlockNumber < highestObservedByCaller.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) - } - - if isOutOfSync { - n.lfcLog.Debugw("finalized block is out of sync", "rpcLatestChainInfo", latest, "rpcHighest", rpcHighest, "highestObservedByCaller", highestObservedByCaller) - } - - return isOutOfSync -} - -// StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle. -func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { - n.stateMu.RLock() - defer n.stateMu.RUnlock() - latest, _ := n.rpc.GetInterceptedChainInfo() - return n.recalculateState(), latest -} - -// HighestUserObservations - returns highest ChainInfo ever observed by external user of the Node -func (n *node[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { - _, highestUserObservations := n.rpc.GetInterceptedChainInfo() - return highestUserObservations -} -func (n *node[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(poolInfoProvider PoolChainInfoProvider) { - n.poolInfoProvider = poolInfoProvider -} - -// 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, nodeStateSyncing: - 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, nodeStateSyncing: - 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(syncIssues syncStatus) { - n.transitionToOutOfSync(func() { - n.lfcLog.Errorw("RPC Node is out of sync", "nodeState", n.state, "syncIssues", syncIssues) - n.wg.Add(1) - go n.outOfSyncLoop(syncIssues) - }) -} - -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.rpc.Close() - 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, nodeStateSyncing: - n.rpc.Close() - n.state = nodeStateUnreachable - default: - panic(transitionFail(n.state, nodeStateUnreachable)) - } - fn() -} - -func (n *node[CHAIN_ID, HEAD, RPC]) declareState(state nodeState) { - if n.getCachedState() == nodeStateClosed { - return - } - switch state { - case nodeStateInvalidChainID: - n.declareInvalidChainID() - case nodeStateUnreachable: - n.declareUnreachable() - case nodeStateSyncing: - n.declareSyncing() - case nodeStateAlive: - n.declareAlive() - default: - panic(fmt.Sprintf("%#v state declaration is not implemented", state)) - } -} - -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, nodeStateSyncing: - n.rpc.Close() - n.state = nodeStateInvalidChainID - default: - panic(transitionFail(n.state, nodeStateInvalidChainID)) - } - fn() -} - -func (n *node[CHAIN_ID, HEAD, RPC]) declareSyncing() { - n.transitionToSyncing(func() { - n.lfcLog.Errorw("RPC Node is syncing", "nodeState", n.state) - n.wg.Add(1) - go n.syncingLoop() - }) -} - -func (n *node[CHAIN_ID, HEAD, RPC]) transitionToSyncing(fn func()) { - promPoolRPCNodeTransitionsToSyncing.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, nodeStateInvalidChainID: - n.rpc.Close() - n.state = nodeStateSyncing - default: - panic(transitionFail(n.state, nodeStateSyncing)) - } - - if !n.nodePoolCfg.NodeIsSyncingEnabled() { - panic("unexpected transition to nodeStateSyncing, while it's disabled") - } - 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_fsm_test.go b/common/client/node_fsm_test.go deleted file mode 100644 index 93460d934a3..00000000000 --- a/common/client/node_fsm_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package client - -import ( - "slices" - "strconv" - "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 TestUnit_Node_StateTransitions(t *testing.T) { - t.Parallel() - - t.Run("setState", func(t *testing.T) { - n := newTestNode(t, testNodeOpts{rpc: nil, config: testNodeConfig{nodeIsSyncingEnabled: true}}) - 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, nodeStateSyncing} - rpc := newMockRPCClient[types.ID, Head](t) - testTransition(t, rpc, testNode.transitionToAlive, destinationState, allowedStates...) - }) - - t.Run("transitionToInSync", func(t *testing.T) { - const destinationState = nodeStateAlive - allowedStates := []nodeState{nodeStateOutOfSync, nodeStateSyncing} - rpc := newMockRPCClient[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 := newMockRPCClient[types.ID, Head](t) - rpc.On("Close") - testTransition(t, rpc, testNode.transitionToOutOfSync, destinationState, allowedStates...) - }) - t.Run("transitionToUnreachable", func(t *testing.T) { - const destinationState = nodeStateUnreachable - allowedStates := []nodeState{nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing} - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("Close") - testTransition(t, rpc, testNode.transitionToUnreachable, destinationState, allowedStates...) - }) - t.Run("transitionToInvalidChain", func(t *testing.T) { - const destinationState = nodeStateInvalidChainID - allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing} - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("Close") - testTransition(t, rpc, testNode.transitionToInvalidChainID, destinationState, allowedStates...) - }) - t.Run("transitionToSyncing", func(t *testing.T) { - const destinationState = nodeStateSyncing - allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID} - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("Close") - testTransition(t, rpc, testNode.transitionToSyncing, destinationState, allowedStates...) - }) - t.Run("transitionToSyncing panics if nodeIsSyncing is disabled", func(t *testing.T) { - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("Close") - node := newTestNode(t, testNodeOpts{rpc: rpc}) - node.setState(nodeStateDialed) - fn := new(fnMock) - defer fn.AssertNotCalled(t) - assert.PanicsWithValue(t, "unexpected transition to nodeStateSyncing, while it's disabled", func() { - node.transitionToSyncing(fn.Fn) - }) - }) -} - -func testTransition(t *testing.T, rpc *mockRPCClient[types.ID, Head], transition func(node testNode, fn func()), destinationState nodeState, allowedStates ...nodeState) { - node := newTestNode(t, testNodeOpts{rpc: rpc, config: testNodeConfig{nodeIsSyncingEnabled: true}}) - 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") - } -} - -func TestNodeState_String(t *testing.T) { - t.Run("Ensure all states are meaningful when converted to string", func(t *testing.T) { - for _, ns := range allNodeStates { - // ensure that string representation is not nodeState(%d) - assert.NotContains(t, ns.String(), strconv.FormatInt(int64(ns), 10), "Expected node state to have readable name") - } - }) -} diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go deleted file mode 100644 index 6ec6a598eb2..00000000000 --- a/common/client/node_lifecycle.go +++ /dev/null @@ -1,700 +0,0 @@ -package client - -import ( - "context" - "fmt" - "math" - "math/big" - "time" - - "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/utils" - bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" - - iutils "github.com/smartcontractkit/chainlink/v2/common/internal/utils" - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -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"}) - promPoolRPCNodeHighestFinalizedBlock = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Name: "pool_rpc_node_highest_finalized_block", - Help: "The highest seen finalized 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) -} - -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() - ctx, cancel := n.newCtx() - defer cancel() - - { - // sanity check - state := n.getCachedState() - 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.chainCfg.NodeNoNewHeadsThreshold() - noNewFinalizedBlocksTimeoutThreshold := n.chainCfg.NoNewFinalizedHeadsThreshold() - pollFailureThreshold := n.nodePoolCfg.PollFailureThreshold() - pollInterval := n.nodePoolCfg.PollInterval() - - lggr := logger.Sugared(n.lfcLog).Named("Alive").With("noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) - lggr.Tracew("Alive loop starting", "nodeState", n.getCachedState()) - - headsSub, err := n.registerNewSubscription(ctx, lggr.With("subscriptionType", "heads"), - n.chainCfg.NodeNoNewHeadsThreshold(), n.rpc.SubscribeToHeads) - if err != nil { - lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.getCachedState(), "err", err) - n.declareUnreachable() - return - } - - defer n.unsubscribeHealthChecks() - - 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") - } - - var finalizedHeadsSub headSubscription[HEAD] - if n.chainCfg.FinalityTagEnabled() { - finalizedHeadsSub, err = n.registerNewSubscription(ctx, lggr.With("subscriptionType", "finalizedHeads"), - n.chainCfg.NoNewFinalizedHeadsThreshold(), n.rpc.SubscribeToFinalizedHeads) - if err != nil { - lggr.Errorw("Failed to subscribe to finalized heads", "err", err) - n.declareUnreachable() - return - } - } - - // Get the latest chain info to use as local highest - localHighestChainInfo, _ := n.rpc.GetInterceptedChainInfo() - var pollFailures uint32 - - for { - select { - case <-ctx.Done(): - return - case <-pollCh: - promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Pinging RPC", "nodeState", n.State(), "pollFailures", pollFailures) - pollCtx, cancel := context.WithTimeout(ctx, pollInterval) - err = n.RPC().Ping(pollCtx) - 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.getCachedState()) - } else { - lggr.Debugw("Ping successful", "nodeState", n.State()) - 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.getCachedState()) - if n.poolInfoProvider != nil { - if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 2 { - lggr.Criticalf("RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) - continue - } - } - n.declareUnreachable() - return - } - if outOfSync, liveNodes := n.isOutOfSyncWithPool(); outOfSync { - // note: there must be another live node for us to be out of sync - if liveNodes < 2 { - lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) - continue - } - n.declareOutOfSync(syncStatusNotInSyncWithPool) - return - } - case bh, open := <-headsSub.Heads: - if !open { - lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.getCachedState()) - n.declareUnreachable() - return - } - receivedNewHead := n.onNewHead(lggr, &localHighestChainInfo, bh) - if receivedNewHead && noNewHeadsTimeoutThreshold > 0 { - headsSub.ResetTimer(noNewHeadsTimeoutThreshold) - } - case err = <-headsSub.Errors: - lggr.Errorw("Subscription was terminated", "err", err, "nodeState", n.getCachedState()) - n.declareUnreachable() - return - case <-headsSub.NoNewHeads: - // 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, localHighestChainInfo.BlockNumber), "nodeState", n.getCachedState(), "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) - if n.poolInfoProvider != nil { - if l, _ := n.poolInfoProvider.LatestChainInfo(); 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 - headsSub.ResetTimer(zombieNodeCheckInterval(noNewHeadsTimeoutThreshold)) - continue - } - } - n.declareOutOfSync(syncStatusNoNewHead) - return - case latestFinalized, open := <-finalizedHeadsSub.Heads: - if !open { - lggr.Errorw("Finalized heads subscription channel unexpectedly closed") - n.declareUnreachable() - return - } - - receivedNewHead := n.onNewFinalizedHead(lggr, &localHighestChainInfo, latestFinalized) - if receivedNewHead && noNewFinalizedBlocksTimeoutThreshold > 0 { - finalizedHeadsSub.ResetTimer(noNewFinalizedBlocksTimeoutThreshold) - } - case <-finalizedHeadsSub.NoNewHeads: - // We haven't received a finalized head on the channel for at least the - // threshold amount of time, mark it broken - lggr.Errorw(fmt.Sprintf("RPC's finalized state is out of sync; no new finalized heads received for %s (last finalized head received was %v)", noNewFinalizedBlocksTimeoutThreshold, localHighestChainInfo.FinalizedBlockNumber), "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber) - if n.poolInfoProvider != nil { - if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 2 { - lggr.Criticalf("RPC's finalized state is 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 - finalizedHeadsSub.ResetTimer(zombieNodeCheckInterval(noNewFinalizedBlocksTimeoutThreshold)) - continue - } - } - n.declareOutOfSync(syncStatusNoNewFinalizedHead) - return - case <-finalizedHeadsSub.Errors: - lggr.Errorw("Finalized heads subscription was terminated", "err", err) - n.declareUnreachable() - return - } - } -} - -func (n *node[CHAIN_ID, HEAD, RPC]) unsubscribeHealthChecks() { - n.stateMu.Lock() - for _, sub := range n.healthCheckSubs { - sub.Unsubscribe() - } - n.healthCheckSubs = []types.Subscription{} - n.stateMu.Unlock() -} - -type headSubscription[HEAD any] struct { - Heads <-chan HEAD - Errors <-chan error - NoNewHeads <-chan time.Time - - noNewHeadsTicker *time.Ticker - sub types.Subscription - cleanUpTasks []func() -} - -func (sub *headSubscription[HEAD]) ResetTimer(duration time.Duration) { - sub.noNewHeadsTicker.Reset(duration) -} - -func (sub *headSubscription[HEAD]) Unsubscribe() { - for _, doCleanUp := range sub.cleanUpTasks { - doCleanUp() - } -} - -func (n *node[CHAIN_ID, HEAD, PRC]) registerNewSubscription(ctx context.Context, lggr logger.SugaredLogger, - noNewDataThreshold time.Duration, newSub func(ctx context.Context) (<-chan HEAD, types.Subscription, error)) (headSubscription[HEAD], error) { - result := headSubscription[HEAD]{} - var err error - var sub types.Subscription - result.Heads, sub, err = newSub(ctx) - if err != nil { - return result, err - } - - result.Errors = sub.Err() - lggr.Debug("Successfully subscribed") - - result.sub = sub - n.stateMu.Lock() - n.healthCheckSubs = append(n.healthCheckSubs, sub) - n.stateMu.Unlock() - - result.cleanUpTasks = append(result.cleanUpTasks, sub.Unsubscribe) - - if noNewDataThreshold > 0 { - lggr.Debugw("Subscription liveness checking enabled") - result.noNewHeadsTicker = time.NewTicker(noNewDataThreshold) - result.NoNewHeads = result.noNewHeadsTicker.C - result.cleanUpTasks = append(result.cleanUpTasks, result.noNewHeadsTicker.Stop) - } else { - lggr.Debug("Subscription liveness checking disabled") - } - - return result, nil -} - -func (n *node[CHAIN_ID, HEAD, RPC]) onNewFinalizedHead(lggr logger.SugaredLogger, chainInfo *ChainInfo, latestFinalized HEAD) bool { - if !latestFinalized.IsValid() { - lggr.Warn("Latest finalized block is not valid") - return false - } - - latestFinalizedBN := latestFinalized.BlockNumber() - lggr.Debugw("Got latest finalized head", "latestFinalized", latestFinalized) - if latestFinalizedBN <= chainInfo.FinalizedBlockNumber { - lggr.Debugw("Ignoring previously seen finalized block number") - return false - } - - promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN)) - chainInfo.FinalizedBlockNumber = latestFinalizedBN - return true -} - -func (n *node[CHAIN_ID, HEAD, RPC]) onNewHead(lggr logger.SugaredLogger, chainInfo *ChainInfo, head HEAD) bool { - if !head.IsValid() { - lggr.Warn("Latest head is not valid") - return false - } - - promPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Debugw("Got head", "head", head) - lggr = lggr.With("latestReceivedBlockNumber", chainInfo.BlockNumber, "blockNumber", head.BlockNumber(), "nodeState", n.getCachedState()) - if head.BlockNumber() <= chainInfo.BlockNumber { - lggr.Debugw("Ignoring previously seen block number") - return false - } - - promPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(head.BlockNumber())) - chainInfo.BlockNumber = head.BlockNumber() - - if !n.chainCfg.FinalityTagEnabled() { - latestFinalizedBN := max(head.BlockNumber()-int64(n.chainCfg.FinalityDepth()), 0) - if latestFinalizedBN > chainInfo.FinalizedBlockNumber { - promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN)) - chainInfo.FinalizedBlockNumber = latestFinalizedBN - } - } - - return true -} - -const ( - msgReceivedBlock = "Received block for RPC node, waiting until back in-sync to mark as live again" - msgReceivedFinalizedBlock = "Received new finalized block for RPC node, waiting until back in-sync to mark as live again" - msgInSync = "RPC node back in sync" -) - -// isOutOfSyncWithPool 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]) isOutOfSyncWithPool() (outOfSync bool, liveNodes int) { - if n.poolInfoProvider == nil { - n.lfcLog.Warn("skipping sync state against the pool - should only occur in tests") - return // skip for tests - } - threshold := n.nodePoolCfg.SyncThreshold() - if threshold == 0 { - return // disabled - } - // Check against best node - ln, ci := n.poolInfoProvider.LatestChainInfo() - localChainInfo, _ := n.rpc.GetInterceptedChainInfo() - mode := n.nodePoolCfg.SelectionMode() - switch mode { - case NodeSelectionModeHighestHead, NodeSelectionModeRoundRobin, NodeSelectionModePriorityLevel: - outOfSync = localChainInfo.BlockNumber < ci.BlockNumber-int64(threshold) - case NodeSelectionModeTotalDifficulty: - bigThreshold := big.NewInt(int64(threshold)) - outOfSync = localChainInfo.TotalDifficulty.Cmp(bigmath.Sub(ci.TotalDifficulty, bigThreshold)) < 0 - default: - panic("unrecognized NodeSelectionMode: " + mode) - } - - if outOfSync && n.getCachedState() == nodeStateAlive { - n.lfcLog.Errorw("RPC endpoint has fallen behind", "blockNumber", localChainInfo.BlockNumber, "bestLatestBlockNumber", ci.BlockNumber, "totalDifficulty", localChainInfo.TotalDifficulty) - } - return outOfSync, ln -} - -// 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(syncIssues syncStatus) { - defer n.wg.Done() - ctx, cancel := n.newCtx() - defer cancel() - - { - // sanity check - state := n.getCachedState() - 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() - - // set logger name to OutOfSync or FinalizedBlockOutOfSync - lggr := logger.Sugared(logger.Named(n.lfcLog, n.getCachedState().String())).With("nodeState", n.getCachedState()) - lggr.Debugw("Trying to revive out-of-sync RPC node") - - // Need to redial since out-of-sync nodes are automatically disconnected - state := n.createVerifiedConn(ctx, lggr) - if state != nodeStateAlive { - n.declareState(state) - return - } - - noNewHeadsTimeoutThreshold := n.chainCfg.NodeNoNewHeadsThreshold() - headsSub, err := n.registerNewSubscription(ctx, lggr.With("subscriptionType", "heads"), - noNewHeadsTimeoutThreshold, n.rpc.SubscribeToHeads) - if err != nil { - lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "err", err) - n.declareUnreachable() - return - } - - defer n.unsubscribeHealthChecks() - - lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node") - - noNewFinalizedBlocksTimeoutThreshold := n.chainCfg.NoNewFinalizedHeadsThreshold() - var finalizedHeadsSub headSubscription[HEAD] - if n.chainCfg.FinalityTagEnabled() { - finalizedHeadsSub, err = n.registerNewSubscription(ctx, lggr.With("subscriptionType", "finalizedHeads"), - noNewFinalizedBlocksTimeoutThreshold, n.rpc.SubscribeToFinalizedHeads) - if err != nil { - lggr.Errorw("Subscribe to finalized heads failed on out-of-sync RPC node", "err", err) - n.declareUnreachable() - return - } - - lggr.Tracew("Successfully subscribed to finalized heads feed on out-of-sync RPC node") - } - - _, localHighestChainInfo := n.rpc.GetInterceptedChainInfo() - for { - if syncIssues == syncStatusSynced { - // 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))) - n.declareInSync() - return - } - - select { - case <-ctx.Done(): - return - case head, open := <-headsSub.Heads: - if !open { - lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.getCachedState()) - n.declareUnreachable() - return - } - - if !n.onNewHead(lggr, &localHighestChainInfo, head) { - continue - } - - // received a new head - clear NoNewHead flag - syncIssues &= ^syncStatusNoNewHead - if outOfSync, _ := n.isOutOfSyncWithPool(); !outOfSync { - // we caught up with the pool - clear NotInSyncWithPool flag - syncIssues &= ^syncStatusNotInSyncWithPool - } else { - // we've received new head, but lagging behind the pool, add NotInSyncWithPool flag to prevent false transition to alive - syncIssues |= syncStatusNotInSyncWithPool - } - - if noNewHeadsTimeoutThreshold > 0 { - headsSub.ResetTimer(noNewHeadsTimeoutThreshold) - } - - lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "syncIssues", syncIssues) - case <-time.After(zombieNodeCheckInterval(noNewHeadsTimeoutThreshold)): - if n.poolInfoProvider != nil { - if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 1 { - lggr.Criticalw("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", "syncIssues", syncIssues) - n.declareInSync() - return - } - } - case err := <-headsSub.Errors: - lggr.Errorw("Subscription was terminated", "err", err) - n.declareUnreachable() - return - case <-headsSub.NoNewHeads: - // we are not resetting the timer, as there is no need to add syncStatusNoNewHead until it's removed on new head. - syncIssues |= syncStatusNoNewHead - lggr.Debugw(fmt.Sprintf("No new heads received for %s. Node stays out-of-sync due to sync issues: %s", noNewHeadsTimeoutThreshold, syncIssues)) - case latestFinalized, open := <-finalizedHeadsSub.Heads: - if !open { - lggr.Errorw("Finalized heads subscription channel unexpectedly closed") - n.declareUnreachable() - return - } - if !latestFinalized.IsValid() { - lggr.Warn("Latest finalized block is not valid") - continue - } - - receivedNewHead := n.onNewFinalizedHead(lggr, &localHighestChainInfo, latestFinalized) - if !receivedNewHead { - continue - } - - // on new finalized head remove NoNewFinalizedHead flag from the mask - syncIssues &= ^syncStatusNoNewFinalizedHead - if noNewFinalizedBlocksTimeoutThreshold > 0 { - finalizedHeadsSub.ResetTimer(noNewFinalizedBlocksTimeoutThreshold) - } - - var highestSeen ChainInfo - if n.poolInfoProvider != nil { - highestSeen = n.poolInfoProvider.HighestUserObservations() - } - - lggr.Debugw(msgReceivedFinalizedBlock, "blockNumber", latestFinalized.BlockNumber(), "poolHighestBlockNumber", highestSeen.FinalizedBlockNumber, "syncIssues", syncIssues) - case err := <-finalizedHeadsSub.Errors: - lggr.Errorw("Finalized head subscription was terminated", "err", err) - n.declareUnreachable() - return - case <-finalizedHeadsSub.NoNewHeads: - // we are not resetting the timer, as there is no need to add syncStatusNoNewFinalizedHead until it's removed on new finalized head. - syncIssues |= syncStatusNoNewFinalizedHead - lggr.Debugw(fmt.Sprintf("No new finalized heads received for %s. Node stays out-of-sync due to sync issues: %s", noNewFinalizedBlocksTimeoutThreshold, syncIssues)) - } - } -} - -func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { - defer n.wg.Done() - ctx, cancel := n.newCtx() - defer cancel() - - { - // sanity check - state := n.getCachedState() - 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 := logger.Sugared(logger.Named(n.lfcLog, "Unreachable")) - lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.getCachedState()) - - dialRetryBackoff := iutils.NewRedialBackoff() - - for { - select { - case <-ctx.Done(): - return - case <-time.After(dialRetryBackoff.Duration()): - lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.getCachedState()) - - err := n.rpc.Dial(ctx) - if err != nil { - lggr.Errorw(fmt.Sprintf("Failed to redial RPC node; still unreachable: %v", err), "err", err, "nodeState", n.getCachedState()) - continue - } - - n.setState(nodeStateDialed) - - state := n.verifyConn(ctx, lggr) - switch state { - case nodeStateUnreachable: - n.setState(nodeStateUnreachable) - continue - case nodeStateAlive: - lggr.Infow(fmt.Sprintf("Successfully redialled and verified RPC node %s. Node was offline for %s", n.String(), time.Since(unreachableAt)), "nodeState", n.getCachedState()) - fallthrough - default: - n.declareState(state) - return - } - } - } -} - -func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { - defer n.wg.Done() - ctx, cancel := n.newCtx() - defer cancel() - - { - // sanity check - state := n.getCachedState() - 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 := logger.Named(n.lfcLog, "InvalidChainID") - - // Need to redial since invalid chain ID nodes are automatically disconnected - state := n.createVerifiedConn(ctx, lggr) - if state != nodeStateInvalidChainID { - n.declareState(state) - return - } - - lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.getCachedState()) - - chainIDRecheckBackoff := iutils.NewRedialBackoff() - - for { - select { - case <-ctx.Done(): - return - case <-time.After(chainIDRecheckBackoff.Duration()): - state := n.verifyConn(ctx, lggr) - switch state { - case nodeStateInvalidChainID: - continue - case nodeStateAlive: - lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.getCachedState()) - fallthrough - default: - n.declareState(state) - return - } - } - } -} - -func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { - defer n.wg.Done() - ctx, cancel := n.newCtx() - defer cancel() - - { - // sanity check - state := n.getCachedState() - switch state { - case nodeStateSyncing: - case nodeStateClosed: - return - default: - panic(fmt.Sprintf("syncingLoop can only run for node in NodeStateSyncing state, got: %s", state)) - } - } - - syncingAt := time.Now() - - lggr := logger.Sugared(logger.Named(n.lfcLog, "Syncing")) - lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with syncing status", n.String()), "nodeState", n.getCachedState()) - // Need to redial since syncing nodes are automatically disconnected - state := n.createVerifiedConn(ctx, lggr) - if state != nodeStateSyncing { - n.declareState(state) - return - } - - recheckBackoff := iutils.NewRedialBackoff() - - for { - select { - case <-ctx.Done(): - return - case <-time.After(recheckBackoff.Duration()): - lggr.Tracew("Trying to recheck if the node is still syncing", "nodeState", n.getCachedState()) - isSyncing, err := n.rpc.IsSyncing(ctx) - if err != nil { - lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.getCachedState()) - n.declareUnreachable() - return - } - - if isSyncing { - lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.getCachedState()) - continue - } - - lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was syncing for %s", time.Since(syncingAt)), "nodeState", n.getCachedState()) - n.declareAlive() - return - } - } -} diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go deleted file mode 100644 index 39c39e318ef..00000000000 --- a/common/client/node_lifecycle_test.go +++ /dev/null @@ -1,1983 +0,0 @@ -package client - -import ( - "errors" - "fmt" - "math/big" - "sync" - "sync/atomic" - "testing" - - "github.com/cometbft/cometbft/libs/rand" - prom "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - - clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks" - "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/common/types/mocks" -) - -func newSub(t *testing.T) *mocks.Subscription { - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)).Maybe() - sub.On("Unsubscribe") - return sub -} - -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) - - 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 := newMockRPCClient[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("SubscribeToHeads", mock.Anything).Return(nil, nil, expectedError).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 := newMockRPCClient[types.ID, Head](t) - - lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) - node := newDialedNode(t, testNodeOpts{ - rpc: rpc, - lggr: lggr, - }) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - 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("SubscribeToHeads", mock.Anything).Return(nil, sub, nil).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 := newSub(t) - opts.rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() - return newDialedNode(t, opts) - } - t.Run("Stays alive and waits for signal", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - lggr, observedLogs := logger.TestObserved(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, "Subscription 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 := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) - lggr, observedLogs := logger.TestObserved(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("Ping", 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("Ping", mock.Anything).Return(nil).Once() - // 3. Return error. If we have not reset the timer, we'll transition to nonAliveState - rpc.On("Ping", mock.Anything).Return(pollError).Once() - // 4. Once during the call, check if node is alive - var ensuredAlive atomic.Bool - rpc.On("Ping", mock.Anything).Return(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("Ping", mock.Anything).Return(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, "Ping 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 := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) - lggr, observedLogs := logger.TestObserved(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("Ping", mock.Anything).Return(pollError) - 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 := newMockRPCClient[types.ID, Head](t) - lggr, observedLogs := logger.TestObserved(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()) }() - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ - BlockNumber: 20, - }).Once() - node.SetPoolChainInfoProvider(poolInfo) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 20}, ChainInfo{BlockNumber: 20}) - pollError := errors.New("failed to get ClientVersion") - rpc.On("Ping", 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 := newMockRPCClient[types.ID, Head](t) - lggr, observedLogs := logger.TestObserved(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()) }() - rpc.On("Ping", mock.Anything).Return(nil) - const mostRecentBlock = 20 - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(10, ChainInfo{ - BlockNumber: syncThreshold + mostRecentBlock + 1, - TotalDifficulty: big.NewInt(10), - }) - node.SetPoolChainInfoProvider(poolInfo) - // 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() - rpc.On("Close").Maybe() - rpc.On("Dial", mock.Anything).Run(func(_ mock.Arguments) { - require.Equal(t, nodeStateOutOfSync, node.State()) - }).Return(errors.New("failed to dial")).Maybe() - node.declareAlive() - tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable") - }) - t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - lggr, observedLogs := logger.TestObserved(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()) }() - rpc.On("Ping", mock.Anything).Return(nil) - const mostRecentBlock = 20 - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ - BlockNumber: syncThreshold + mostRecentBlock + 1, - TotalDifficulty: big.NewInt(10), - }) - node.SetPoolChainInfoProvider(poolInfo) - 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 := newMockRPCClient[types.ID, Head](t) - lggr, observedLogs := logger.TestObserved(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()) }() - rpc.On("Ping", mock.Anything).Return(nil) - const mostRecentBlock = 20 - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) - node.declareAlive() - tests.AssertLogCountEventually(t, observedLogs, "Ping 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 := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) - node := newSubscribedNode(t, testNodeOpts{ - config: testNodeConfig{}, - chainConfig: clientMocks.ChainConfig{ - NoNewHeadsThresholdVal: 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() - 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 := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newSubscribedNode(t, testNodeOpts{ - config: testNodeConfig{}, - lggr: lggr, - chainConfig: clientMocks.ChainConfig{ - NoNewHeadsThresholdVal: tests.TestInterval, - }, - rpc: rpc, - }) - defer func() { assert.NoError(t, node.close()) }() - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ - BlockNumber: 20, - TotalDifficulty: big.NewInt(10), - }).Once() - node.SetPoolChainInfoProvider(poolInfo) - 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 := newMockRPCClient[types.ID, Head](t) - sub := newSub(t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - ch := make(chan Head) - rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { - close(ch) - }).Return((<-chan Head)(ch), sub, nil).Once() - lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) - node := newDialedNode(t, testNodeOpts{ - lggr: lggr, - config: testNodeConfig{}, - chainConfig: clientMocks.ChainConfig{ - NoNewHeadsThresholdVal: tests.TestInterval, - }, - rpc: rpc, - }) - defer func() { assert.NoError(t, node.close()) }() - 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("If finality tag is not enabled updates finalized block metric using finality depth and latest head", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - sub := newSub(t) - const blockNumber = 1000 - const finalityDepth = 10 - const expectedBlock = 990 - ch := make(chan Head) - rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - go writeHeads(t, ch, head{BlockNumber: blockNumber - 1}, head{BlockNumber: blockNumber}, head{BlockNumber: blockNumber - 1}) - }).Return((<-chan Head)(ch), sub, nil).Once() - name := "node-" + rand.Str(5) - node := newDialedNode(t, testNodeOpts{ - config: testNodeConfig{}, - chainConfig: clientMocks.ChainConfig{FinalityDepthVal: finalityDepth}, - rpc: rpc, - name: name, - chainID: big.NewInt(1), - }) - defer func() { assert.NoError(t, node.close()) }() - node.declareAlive() - tests.AssertEventually(t, func() bool { - metric, err := promPoolRPCNodeHighestFinalizedBlock.GetMetricWithLabelValues(big.NewInt(1).String(), name) - require.NoError(t, err) - var m = &prom.Metric{} - require.NoError(t, metric.Write(m)) - return float64(expectedBlock) == m.Gauge.GetValue() - }) - }) - t.Run("If fails to subscribe to latest finalized blocks, transitions to unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - sub := newSub(t) - rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() - expectedError := errors.New("failed to subscribe to finalized heads") - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(nil, sub, expectedError).Once() - lggr := logger.Test(t) - node := newDialedNode(t, testNodeOpts{ - config: testNodeConfig{ - finalizedBlockPollInterval: tests.TestInterval, - }, - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - }, - rpc: rpc, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - node.declareAlive() - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("Logs warning if latest finalized block is not valid", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - sub := newSub(t) - rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() - ch := make(chan Head, 1) - head := newMockHead(t) - head.On("IsValid").Return(false) - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Run(func(args mock.Arguments) { - ch <- head - }).Return((<-chan Head)(ch), sub, nil).Once() - - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newDialedNode(t, testNodeOpts{ - config: testNodeConfig{}, - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - }, - rpc: rpc, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - node.declareAlive() - tests.AssertLogEventually(t, observedLogs, "Latest finalized block is not valid") - }) - t.Run("On new finalized block updates corresponding metric", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - const expectedBlock = 1101 - const finalityDepth = 10 - ch := make(chan Head) - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), newSub(t), nil).Once() - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - name := "node-" + rand.Str(5) - node := newSubscribedNode(t, testNodeOpts{ - config: testNodeConfig{}, - chainConfig: clientMocks.ChainConfig{ - FinalityDepthVal: finalityDepth, - IsFinalityTagEnabled: true, - }, - rpc: rpc, - name: name, - chainID: big.NewInt(1), - }) - defer func() { assert.NoError(t, node.close()) }() - node.declareAlive() - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - writeHeads(t, ch, head{BlockNumber: expectedBlock - 1}, head{BlockNumber: expectedBlock}, head{BlockNumber: expectedBlock - 1}) - }() - tests.AssertEventually(t, func() bool { - metric, err := promPoolRPCNodeHighestFinalizedBlock.GetMetricWithLabelValues(big.NewInt(1).String(), name) - require.NoError(t, err) - var m = &prom.Metric{} - require.NoError(t, metric.Write(m)) - return float64(expectedBlock) == m.Gauge.GetValue() - }) - }) - t.Run("If finalized heads channel is closed, transitions to unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - ch := make(chan Head) - close(ch) - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), newSub(t), nil).Once() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newSubscribedNode(t, testNodeOpts{ - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - }, - rpc: rpc, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() - node.declareAlive() - tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription channel unexpectedly closed") - tests.AssertEventually(t, func() bool { - return nodeStateUnreachable == node.State() - }) - }) - t.Run("when no new finalized heads received for threshold, transitions to out of sync", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - ch := make(chan Head, 1) - ch <- head{BlockNumber: 10}.ToMockHead(t) - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), newSub(t), nil).Once() - lggr, observed := logger.TestObserved(t, zap.DebugLevel) - noNewFinalizedHeadsThreshold := tests.TestInterval - node := newSubscribedNode(t, testNodeOpts{ - config: testNodeConfig{}, - chainConfig: clientMocks.ChainConfig{ - NoNewFinalizedHeadsThresholdVal: noNewFinalizedHeadsThreshold, - IsFinalityTagEnabled: true, - }, - rpc: rpc, - lggr: lggr, - }) - 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() - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() - node.declareAlive() - tests.AssertLogEventually(t, observed, fmt.Sprintf("RPC's finalized state is out of sync; no new finalized heads received for %s (last finalized head received was 10)", noNewFinalizedHeadsThreshold)) - 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 finalized heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), newSub(t), nil).Once() - lggr, observed := logger.TestObserved(t, zap.DebugLevel) - noNewFinalizedHeadsThreshold := tests.TestInterval - node := newSubscribedNode(t, testNodeOpts{ - config: testNodeConfig{}, - chainConfig: clientMocks.ChainConfig{ - NoNewFinalizedHeadsThresholdVal: noNewFinalizedHeadsThreshold, - IsFinalityTagEnabled: true, - }, - rpc: rpc, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ - BlockNumber: 20, - TotalDifficulty: big.NewInt(10), - }).Once() - node.SetPoolChainInfoProvider(poolInfo) - node.declareAlive() - tests.AssertLogEventually(t, observed, fmt.Sprintf("RPC's finalized state is out of sync; %s %s", msgCannotDisable, msgDegradedState)) - assert.Equal(t, nodeStateAlive, node.State()) - }) - t.Run("If finalized subscription returns an error, transitions to unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - sub := mocks.NewSubscription(t) - errCh := make(chan error, 1) - errCh <- errors.New("subscription failed") - sub.On("Err").Return((<-chan error)(errCh)) - sub.On("Unsubscribe").Once() - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(nil), sub, nil).Once() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newSubscribedNode(t, testNodeOpts{ - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - }, - rpc: rpc, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() - node.declareAlive() - tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription was terminated") - tests.AssertEventually(t, func() bool { - return nodeStateUnreachable == node.State() - }) - }) -} - -type head struct { - BlockNumber int64 - BlockDifficulty *big.Int -} - -func (h head) ToMockHead(t *testing.T) *mockHead { - m := newMockHead(t) - m.On("BlockNumber").Return(h.BlockNumber).Maybe() - m.On("BlockDifficulty").Return(h.BlockDifficulty).Maybe() - m.On("IsValid").Return(true).Maybe() - return m -} - -func writeHeads(t *testing.T, ch chan<- Head, heads ...head) { - for _, head := range heads { - h := head.ToMockHead(t) - select { - case ch <- h: - case <-tests.Context(t).Done(): - return - } - } -} - -func setupRPCForAliveLoop(t *testing.T, rpc *mockRPCClient[types.ID, Head]) { - rpc.On("Dial", mock.Anything).Return(nil).Maybe() - aliveSubscription := mocks.NewSubscription(t) - aliveSubscription.On("Err").Return(nil).Maybe() - aliveSubscription.On("Unsubscribe").Maybe() - rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), aliveSubscription, nil).Maybe() - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), aliveSubscription, nil).Maybe() - rpc.On("SetAliveLoopSub", mock.Anything).Maybe() - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).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) - 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.outOfSyncLoop(syncStatusNotInSyncWithPool) - }) - t.Run("on old blocks stays outOfSync and returns on close", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr := logger.Test(t) - 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() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 0}, ChainInfo{BlockNumber: 13}).Once() - - outOfSyncSubscription := mocks.NewSubscription(t) - outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) - outOfSyncSubscription.On("Unsubscribe").Once() - heads := []head{{BlockNumber: 7}, {BlockNumber: 11}, {BlockNumber: 13}} - ch := make(chan Head) - var wg sync.WaitGroup - wg.Add(1) - rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { - go func() { - defer wg.Done() - writeHeads(t, ch, heads...) - }() - }).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once() - - rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() - - node.declareOutOfSync(syncStatusNoNewHead) - // wait until all heads are consumed - wg.Wait() - assert.Equal(t, nodeStateOutOfSync, node.State()) - }) - t.Run("if initial dial fails, transitions to unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[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(syncStatusNoNewHead) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("if fail to get chainID, transitions to unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - chainID := types.RandomID() - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: chainID, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("ChainID", mock.Anything).Return(chainID, nil) - // for out-of-sync - rpc.On("Dial", mock.Anything).Return(nil).Once() - // for unreachable - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() - 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("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) - - 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(syncStatusNoNewHead) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("if chainID does not match, transitions to invalidChainID", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[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()) }() - - // one for out-of-sync & one for invalid chainID - rpc.On("Dial", mock.Anything).Return(nil).Twice() - - // might be called multiple times - rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) - node.declareOutOfSync(syncStatusNoNewHead) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateInvalidChainID - }) - }) - t.Run("if syncing, transitions to syncing", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.NewIDFromInt(10) - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(nil) - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) - - // might be called multiple times - rpc.On("IsSyncing", mock.Anything).Return(true, nil) - node.declareOutOfSync(syncStatusNoNewHead) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateSyncing - }) - }) - t.Run("if fails to fetch syncing status, transitions to unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.NewIDFromInt(10) - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - defer func() { assert.NoError(t, node.close()) }() - - // one for out-of-sync - rpc.On("Dial", mock.Anything).Return(nil).Once() - - // for unreachable - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - // might be called multiple times - rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing")) - node.declareOutOfSync(syncStatusNoNewHead) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("if fails to subscribe, becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[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() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - expectedError := errors.New("failed to subscribe") - rpc.On("SubscribeToHeads", mock.Anything).Return(nil, nil, expectedError).Once() - - rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() - node.declareOutOfSync(syncStatusNoNewHead) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("on subscription termination becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).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("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() - rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() - node.declareOutOfSync(syncStatusNoNewHead) - 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 := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - - sub := newSub(t) - ch := make(chan Head) - rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { - close(ch) - }).Return((<-chan Head)(ch), sub, nil).Once() - rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() - node.declareOutOfSync(syncStatusNoNewHead) - 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 := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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() - 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 - ch := make(chan Head) - rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { - go writeHeads(t, ch, head{BlockNumber: highestBlock - 1}, head{BlockNumber: highestBlock}, head{BlockNumber: highestBlock + 1}) - }).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once() - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: highestBlock}, ChainInfo{BlockNumber: highestBlock}) - setupRPCForAliveLoop(t, rpc) - - node.declareOutOfSync(syncStatusNoNewHead) - 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 := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newAliveNode(t, testNodeOpts{ - chainConfig: clientMocks.ChainConfig{ - NoNewHeadsThresholdVal: tests.TestInterval, - }, - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(0, ChainInfo{ - BlockNumber: 100, - TotalDifficulty: big.NewInt(200), - }) - node.SetPoolChainInfoProvider(poolInfo) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) - - rpc.On("Dial", mock.Anything).Return(nil).Once() - 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("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), outOfSyncSubscription, nil).Once() - setupRPCForAliveLoop(t, rpc) - - node.declareOutOfSync(syncStatusNoNewHead) - 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 - }) - }) - t.Run("Stays out-of-sync if received new head, but lags behind pool", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newAliveNode(t, testNodeOpts{ - chainConfig: clientMocks.ChainConfig{ - NoNewHeadsThresholdVal: tests.TestInterval, - }, - config: testNodeConfig{ - syncThreshold: 1, - selectionMode: NodeSelectionModeHighestHead, - }, - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - poolInfo := newMockPoolChainInfoProvider(t) - const highestBlock = 20 - poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ - BlockNumber: highestBlock * 2, - TotalDifficulty: big.NewInt(200), - }) - node.SetPoolChainInfoProvider(poolInfo) - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{BlockNumber: highestBlock}) - - rpc.On("Dial", mock.Anything).Return(nil).Once() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - - outOfSyncSubscription := mocks.NewSubscription(t) - outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) - outOfSyncSubscription.On("Unsubscribe").Once() - ch := make(chan Head) - rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { - go writeHeads(t, ch, head{BlockNumber: highestBlock - 1}, head{BlockNumber: highestBlock}, head{BlockNumber: highestBlock + 1}) - }).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once() - - node.declareOutOfSync(syncStatusNoNewHead) - tests.AssertLogEventually(t, observedLogs, msgReceivedBlock) - tests.AssertLogEventually(t, observedLogs, "No new heads received for") - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateOutOfSync - }) - }) - - // creates RPC mock with all calls necessary to create heads subscription that won't produce any events - newRPCWithNoOpHeads := func(t *testing.T, chainID types.ID) *mockRPCClient[types.ID, Head] { - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("Dial", mock.Anything).Return(nil).Once() - rpc.On("ChainID", mock.Anything).Return(chainID, nil).Once() - sub := newSub(t) - rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() - return rpc - } - - t.Run("if fails to subscribe to finalized, becomes unreachable", func(t *testing.T) { - t.Parallel() - nodeChainID := types.RandomID() - rpc := newRPCWithNoOpHeads(t, nodeChainID) - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - }, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(nil), nil, errors.New("failed to subscribe")).Once() - // unreachable - rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() - - node.declareOutOfSync(syncStatusNoNewHead) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("on subscription termination becomes unreachable", func(t *testing.T) { - t.Parallel() - nodeChainID := types.RandomID() - rpc := newRPCWithNoOpHeads(t, nodeChainID) - lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - }, - }) - defer func() { assert.NoError(t, node.close()) }() - - 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("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) - - // unreachable - rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() - node.declareOutOfSync(syncStatusNoNewHead) - tests.AssertLogEventually(t, observedLogs, "Finalized head 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() - nodeChainID := types.RandomID() - rpc := newRPCWithNoOpHeads(t, nodeChainID) - lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - }, - }) - defer func() { assert.NoError(t, node.close()) }() - - sub := newSub(t) - - ch := make(chan Head) - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Run(func(args mock.Arguments) { - close(ch) - }).Return((<-chan Head)(ch), sub, nil).Once() - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) - // unreachable - rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() - node.declareOutOfSync(syncStatusNoNewHead) - tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription channel unexpectedly closed") - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("becomes alive on new finalized block", func(t *testing.T) { - t.Parallel() - nodeChainID := types.RandomID() - rpc := newRPCWithNoOpHeads(t, nodeChainID) - lggr := logger.Test(t) - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - NoNewFinalizedHeadsThresholdVal: tests.TestInterval, - }, - }) - defer func() { assert.NoError(t, node.close()) }() - - const highestBlock = 13 - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{FinalizedBlockNumber: highestBlock}, ChainInfo{FinalizedBlockNumber: highestBlock}) - - outOfSyncSubscription := mocks.NewSubscription(t) - outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) - outOfSyncSubscription.On("Unsubscribe").Once() - ch := make(chan Head) - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once() - - setupRPCForAliveLoop(t, rpc) - - node.declareOutOfSync(syncStatusNoNewFinalizedHead) - heads := []head{{BlockNumber: highestBlock - 1}, {BlockNumber: highestBlock}} - writeHeads(t, ch, heads...) - assert.Equal(t, nodeStateOutOfSync, node.State()) - writeHeads(t, ch, head{BlockNumber: highestBlock + 1}) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateAlive - }) - }) - t.Run("adds finalized block is not increasing flag, if there is no new finalized heads for too long", func(t *testing.T) { - t.Parallel() - nodeChainID := types.RandomID() - rpc := newRPCWithNoOpHeads(t, nodeChainID) - lggr, observed := logger.TestObserved(t, zap.DebugLevel) - const noNewFinalizedHeads = tests.TestInterval - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - chainConfig: clientMocks.ChainConfig{ - IsFinalityTagEnabled: true, - NoNewFinalizedHeadsThresholdVal: noNewFinalizedHeads, - }, - }) - defer func() { assert.NoError(t, node.close()) }() - - const highestBlock = 13 - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{FinalizedBlockNumber: highestBlock}) - - outOfSyncSubscription := mocks.NewSubscription(t) - outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) - outOfSyncSubscription.On("Unsubscribe").Once() - ch := make(chan Head) - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return((<-chan Head)(ch), outOfSyncSubscription, nil).Once() - - node.declareOutOfSync(syncStatusNotInSyncWithPool) - heads := []head{{BlockNumber: highestBlock - 1}, {BlockNumber: highestBlock}} - writeHeads(t, ch, heads...) - assert.Equal(t, nodeStateOutOfSync, node.State()) - tests.AssertLogEventually(t, observed, fmt.Sprintf("No new finalized heads received for %s. Node stays "+ - "out-of-sync due to sync issues: NotInSyncWithRPCPool,NoNewFinalizedHead", noNewFinalizedHeads)) - }) -} - -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) - - 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 := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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 := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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 verify chain ID for node", 2) - }) - t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[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 syncing status check failure, keeps trying", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - 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, nil) - rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing status")) - node.declareUnreachable() - tests.AssertLogCountEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status", 2) - }) - t.Run("on syncing, transitions to syncing state", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(nil) - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) - rpc.On("IsSyncing", mock.Anything).Return(true, nil) - - setupRPCForAliveLoop(t, rpc) - - node.declareUnreachable() - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateSyncing - }) - }) - t.Run("on successful verification becomes alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - node := newAliveNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) - rpc.On("IsSyncing", mock.Anything).Return(false, nil) - setupRPCForAliveLoop(t, rpc) - - node.declareUnreachable() - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateAlive - }) - }) - t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[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) - - 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 invalid dial becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - node := newDialedNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) - rpc.On("Close") - - node.declareInvalidChainID() - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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")) - // once for chainID and maybe another one for unreachable - rpc.On("Dial", mock.Anything).Return(nil).Once() - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() - - node.declareInvalidChainID() - tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("on chainID mismatch keeps trying", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.NewIDFromInt(10) - rpcChainID := types.NewIDFromInt(11) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newDialedNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(nil).Once() - 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 successful verification without isSyncing becomes alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.NewIDFromInt(10) - rpcChainID := types.NewIDFromInt(11) - node := newDialedNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - }) - defer func() { assert.NoError(t, node.close()) }() - - setupRPCForAliveLoop(t, rpc) - rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - - node.declareInvalidChainID() - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateAlive - }) - }) - t.Run("on successful verification becomes alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.NewIDFromInt(10) - rpcChainID := types.NewIDFromInt(11) - node := newDialedNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once() - - 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) - - return node - } - t.Run("if fails on initial dial, becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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")) - 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 := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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")) - err := node.Start(tests.Context(t)) - assert.NoError(t, err) - tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") - 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 := newMockRPCClient[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) - err := node.Start(tests.Context(t)) - assert.NoError(t, err) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateInvalidChainID - }) - }) - t.Run("if syncing verification fails, becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(nil).Once() - - rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { - assert.Equal(t, nodeStateDialed, node.State()) - }).Return(nodeChainID, nil).Once() - rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing status")) - rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")) - err := node.Start(tests.Context(t)) - assert.NoError(t, err) - tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status") - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("on isSyncing transitions to syncing", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.NewIDFromInt(10) - node := newNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(nil) - - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) - rpc.On("IsSyncing", mock.Anything).Return(true, nil) - err := node.Start(tests.Context(t)) - assert.NoError(t, err) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateSyncing - }) - }) - t.Run("on successful verification becomes alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - node := newNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - config: testNodeConfig{nodeIsSyncingEnabled: true}, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) - rpc.On("IsSyncing", mock.Anything).Return(false, nil) - setupRPCForAliveLoop(t, rpc) - - err := node.Start(tests.Context(t)) - assert.NoError(t, err) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateAlive - }) - }) - t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - node := newNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - }) - defer func() { assert.NoError(t, node.close()) }() - - 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_outOfSyncWithPool(t *testing.T) { - t.Parallel() - t.Run("skip if nLiveNodes is not configured", func(t *testing.T) { - node := newTestNode(t, testNodeOpts{}) - outOfSync, liveNodes := node.isOutOfSyncWithPool() - 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{}) - poolInfo := newMockPoolChainInfoProvider(t) - node.SetPoolChainInfoProvider(poolInfo) - outOfSync, liveNodes := node.isOutOfSyncWithPool() - 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}, - }) - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(1, ChainInfo{}).Once() - node.SetPoolChainInfoProvider(poolInfo) - assert.Panics(t, func() { - _, _ = node.isOutOfSyncWithPool() - }) - }) - 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, - }, - }) - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(nodesNum, ChainInfo{ - BlockNumber: highestBlock, - TotalDifficulty: big.NewInt(totalDifficulty), - }) - node.SetPoolChainInfoProvider(poolInfo) - for _, td := range []int64{totalDifficulty - syncThreshold - 1, totalDifficulty - syncThreshold, totalDifficulty, totalDifficulty + 1} { - for _, testCase := range testCases { - t.Run(fmt.Sprintf("%s: SelectionModeVal: %s: total difficulty: %d", testCase.name, selectionMode, td), func(t *testing.T) { - chainInfo := ChainInfo{BlockNumber: testCase.blockNumber, TotalDifficulty: big.NewInt(td)} - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(chainInfo, ChainInfo{}).Once() - node.rpc = rpc - outOfSync, liveNodes := node.isOutOfSyncWithPool() - 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, - }, - }) - - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("LatestChainInfo").Return(nodesNum, ChainInfo{ - BlockNumber: highestBlock, - TotalDifficulty: big.NewInt(totalDifficulty), - }) - node.SetPoolChainInfoProvider(poolInfo) - for _, hb := range []int64{highestBlock - syncThreshold - 1, highestBlock - syncThreshold, highestBlock, highestBlock + 1} { - for _, testCase := range testCases { - t.Run(fmt.Sprintf("%s: SelectionModeVal: %s: highest block: %d", testCase.name, NodeSelectionModeTotalDifficulty, hb), func(t *testing.T) { - chainInfo := ChainInfo{BlockNumber: hb, TotalDifficulty: big.NewInt(testCase.totalDifficulty)} - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(chainInfo, ChainInfo{}).Once() - node.rpc = rpc - outOfSync, liveNodes := node.isOutOfSyncWithPool() - assert.Equal(t, nodesNum, liveNodes) - assert.Equal(t, testCase.outOfSync, outOfSync) - }) - } - } - }) -} - -func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { - t.Parallel() - newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { - opts.config.nodeIsSyncingEnabled = true - node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil) - - 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.syncingLoop() - }) - t.Run("on invalid dial becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - node := newDialedNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) - - node.declareSyncing() - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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")) - - // once for syncing and maybe another one for unreachable - rpc.On("Dial", mock.Anything).Return(nil).Once() - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() - node.declareSyncing() - tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("on chainID mismatch transitions to invalidChainID", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.NewIDFromInt(10) - rpcChainID := types.NewIDFromInt(11) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newDialedNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - lggr: lggr, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(nil).Twice() - - rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) - node.declareSyncing() - 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 failed Syncing check - becomes unreachable", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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, nil).Once() - // first one is needed to enter internal loop - rpc.On("IsSyncing", mock.Anything).Return(true, nil).Once() - rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check if syncing")).Once() - rpc.On("Dial", mock.Anything).Return(nil).Once() - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() - - node.declareSyncing() - tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status") - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateUnreachable - }) - }) - t.Run("on IsSyncing - keeps trying", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestObserved(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, nil).Once() - rpc.On("IsSyncing", mock.Anything).Return(true, nil) - rpc.On("Dial", mock.Anything).Return(nil).Once() - - node.declareSyncing() - tests.AssertLogCountEventually(t, observedLogs, "Verification failed: Node is syncing", 2) - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateSyncing - }) - }) - t.Run("on successful verification becomes alive", func(t *testing.T) { - t.Parallel() - rpc := newMockRPCClient[types.ID, Head](t) - nodeChainID := types.RandomID() - node := newDialedNode(t, testNodeOpts{ - rpc: rpc, - chainID: nodeChainID, - }) - defer func() { assert.NoError(t, node.close()) }() - - rpc.On("Dial", mock.Anything).Return(nil).Once() - - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - rpc.On("IsSyncing", mock.Anything).Return(true, nil).Once() - rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once() - - setupRPCForAliveLoop(t, rpc) - - node.declareSyncing() - tests.AssertEventually(t, func() bool { - return node.State() == nodeStateAlive - }) - }) -} - -func TestNode_State(t *testing.T) { - t.Run("If not Alive, returns as is", func(t *testing.T) { - for state := nodeState(0); state < nodeStateLen; state++ { - if state == nodeStateAlive { - continue - } - - node := newTestNode(t, testNodeOpts{}) - node.setState(state) - assert.Equal(t, state, node.State()) - } - }) - t.Run("If repeatable read is not enforced, returns alive", func(t *testing.T) { - node := newTestNode(t, testNodeOpts{}) - node.setState(nodeStateAlive) - assert.Equal(t, nodeStateAlive, node.State()) - }) - testCases := []struct { - Name string - FinalizedBlockOffsetVal uint32 - IsFinalityTagEnabled bool - PoolChainInfo ChainInfo - NodeChainInfo ChainInfo - ExpectedState nodeState - }{ - { - Name: "If finality lag does not exceeds offset, returns alive (FinalityDepth)", - FinalizedBlockOffsetVal: 15, - PoolChainInfo: ChainInfo{ - BlockNumber: 20, - }, - NodeChainInfo: ChainInfo{ - BlockNumber: 5, - }, - ExpectedState: nodeStateAlive, - }, - { - Name: "If finality lag does not exceeds offset, returns alive (FinalityTag)", - FinalizedBlockOffsetVal: 15, - IsFinalityTagEnabled: true, - PoolChainInfo: ChainInfo{ - FinalizedBlockNumber: 20, - }, - NodeChainInfo: ChainInfo{ - FinalizedBlockNumber: 5, - }, - ExpectedState: nodeStateAlive, - }, - { - Name: "If finality lag exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityDepth)", - FinalizedBlockOffsetVal: 15, - PoolChainInfo: ChainInfo{ - BlockNumber: 20, - }, - NodeChainInfo: ChainInfo{ - BlockNumber: 4, - }, - ExpectedState: nodeStateFinalizedBlockOutOfSync, - }, - { - Name: "If finality lag exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityTag)", - FinalizedBlockOffsetVal: 15, - IsFinalityTagEnabled: true, - PoolChainInfo: ChainInfo{ - FinalizedBlockNumber: 20, - }, - NodeChainInfo: ChainInfo{ - FinalizedBlockNumber: 4, - }, - ExpectedState: nodeStateFinalizedBlockOutOfSync, - }, - } - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - rpc := newMockRPCClient[types.ID, Head](t) - rpc.On("GetInterceptedChainInfo").Return(tc.NodeChainInfo, tc.PoolChainInfo).Once() - node := newTestNode(t, testNodeOpts{ - config: testNodeConfig{ - enforceRepeatableRead: true, - }, - chainConfig: clientMocks.ChainConfig{ - FinalizedBlockOffsetVal: tc.FinalizedBlockOffsetVal, - IsFinalityTagEnabled: tc.IsFinalityTagEnabled, - }, - rpc: rpc, - }) - poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("HighestUserObservations").Return(tc.PoolChainInfo).Once() - node.SetPoolChainInfoProvider(poolInfo) - node.setState(nodeStateAlive) - assert.Equal(t, tc.ExpectedState, node.State()) - }) - } -} diff --git a/common/client/node_selector.go b/common/client/node_selector.go deleted file mode 100644 index 372b521bb1c..00000000000 --- a/common/client/node_selector.go +++ /dev/null @@ -1,43 +0,0 @@ -package client - -import ( - "fmt" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -const ( - NodeSelectionModeHighestHead = "HighestHead" - NodeSelectionModeRoundRobin = "RoundRobin" - NodeSelectionModeTotalDifficulty = "TotalDifficulty" - NodeSelectionModePriorityLevel = "PriorityLevel" -) - -type NodeSelector[ - CHAIN_ID types.ID, - RPC any, -] interface { - // Select returns a Node, or nil if none can be selected. - // Implementation must be thread-safe. - Select() Node[CHAIN_ID, RPC] - // Name returns the strategy name, e.g. "HighestHead" or "RoundRobin" - Name() string -} - -func newNodeSelector[ - CHAIN_ID types.ID, - RPC any, -](selectionMode string, nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { - switch selectionMode { - case NodeSelectionModeHighestHead: - return NewHighestHeadNodeSelector[CHAIN_ID, RPC](nodes) - case NodeSelectionModeRoundRobin: - return NewRoundRobinSelector[CHAIN_ID, RPC](nodes) - case NodeSelectionModeTotalDifficulty: - return NewTotalDifficultyNodeSelector[CHAIN_ID, RPC](nodes) - case NodeSelectionModePriorityLevel: - return NewPriorityLevelNodeSelector[CHAIN_ID, RPC](nodes) - default: - panic(fmt.Sprintf("unsupported NodeSelectionMode: %s", selectionMode)) - } -} diff --git a/common/client/node_selector_highest_head.go b/common/client/node_selector_highest_head.go deleted file mode 100644 index 454584a77e1..00000000000 --- a/common/client/node_selector_highest_head.go +++ /dev/null @@ -1,40 +0,0 @@ -package client - -import ( - "math" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -type highestHeadNodeSelector[ - CHAIN_ID types.ID, - RPC any, -] []Node[CHAIN_ID, RPC] - -func NewHighestHeadNodeSelector[ - CHAIN_ID types.ID, - RPC any, -](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { - return highestHeadNodeSelector[CHAIN_ID, RPC](nodes) -} - -func (s highestHeadNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { - var highestHeadNumber int64 = math.MinInt64 - var highestHeadNodes []Node[CHAIN_ID, RPC] - for _, n := range s { - state, currentChainInfo := n.StateAndLatest() - currentHeadNumber := currentChainInfo.BlockNumber - if state == nodeStateAlive && currentHeadNumber >= highestHeadNumber { - if highestHeadNumber < currentHeadNumber { - highestHeadNumber = currentHeadNumber - highestHeadNodes = nil - } - highestHeadNodes = append(highestHeadNodes, n) - } - } - return firstOrHighestPriority(highestHeadNodes) -} - -func (s highestHeadNodeSelector[CHAIN_ID, RPC]) Name() string { - return NodeSelectionModeHighestHead -} diff --git a/common/client/node_selector_highest_head_test.go b/common/client/node_selector_highest_head_test.go deleted file mode 100644 index 9d85c688e05..00000000000 --- a/common/client/node_selector_highest_head_test.go +++ /dev/null @@ -1,176 +0,0 @@ -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, RPCClient[types.ID, Head]](NodeSelectionModeHighestHead, nil) - assert.Equal(t, selector.Name(), NodeSelectionModeHighestHead) -} - -func TestHighestHeadNodeSelector(t *testing.T) { - t.Parallel() - - type nodeClient RPCClient[types.ID, Head] - - var nodes []Node[types.ID, nodeClient] - - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, nodeClient](t) - if i == 0 { - // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) - } else if i == 1 { - // second node is alive, LatestReceivedBlockNumber = 1 - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) - } else { - // third node is alive, LatestReceivedBlockNumber = 2 (best node) - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) - } - node.On("Order").Maybe().Return(int32(1)) - nodes = append(nodes, node) - } - - selector := newNodeSelector[types.ID, 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, nodeClient](t) - // fourth node is alive, LatestReceivedBlockNumber = 2 (same as 3rd) - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) - 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, nodeClient](t) - // fifth node is alive, LatestReceivedBlockNumber = 3 (better than 3rd and 4th) - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) - 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, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) - node1.On("Order").Return(int32(1)) - node2 := newMockNode[types.ID, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) - node2.On("Order").Return(int32(1)) - selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, nodeClient]{node1, node2}) - assert.Same(t, node1, selector.Select()) - }) -} - -func TestHighestHeadNodeSelector_None(t *testing.T) { - t.Parallel() - - type nodeClient RPCClient[types.ID, Head] - var nodes []Node[types.ID, nodeClient] - - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, nodeClient](t) - if i == 0 { - // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) - } else { - // others are unreachable - node.On("StateAndLatest").Return(nodeStateUnreachable, ChainInfo{BlockNumber: int64(-1)}) - } - nodes = append(nodes, node) - } - - selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) - assert.Nil(t, selector.Select()) -} - -func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { - t.Parallel() - - type nodeClient RPCClient[types.ID, Head] - var nodes []Node[types.ID, nodeClient] - - t.Run("same head and order", func(t *testing.T) { - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, nodeClient](t) - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) - 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, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) - node1.On("Order").Return(int32(3)) - - node2 := newMockNode[types.ID, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) - node2.On("Order").Return(int32(1)) - - node3 := newMockNode[types.ID, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) - node3.On("Order").Return(int32(2)) - - nodes := []Node[types.ID, 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, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) - node1.On("Order").Maybe().Return(int32(3)) - - node2 := newMockNode[types.ID, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) - node2.On("Order").Maybe().Return(int32(3)) - - node3 := newMockNode[types.ID, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) - node3.On("Order").Return(int32(3)) - - nodes := []Node[types.ID, 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, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) - node1.On("Order").Maybe().Return(int32(3)) - - node2 := newMockNode[types.ID, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) - node2.On("Order").Maybe().Return(int32(4)) - - node3 := newMockNode[types.ID, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) - node3.On("Order").Maybe().Return(int32(3)) - - node4 := newMockNode[types.ID, nodeClient](t) - node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) - node4.On("Order").Maybe().Return(int32(1)) - - nodes := []Node[types.ID, 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.go b/common/client/node_selector_priority_level.go deleted file mode 100644 index 6d6784fb216..00000000000 --- a/common/client/node_selector_priority_level.go +++ /dev/null @@ -1,123 +0,0 @@ -package client - -import ( - "math" - "sort" - "sync/atomic" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -type priorityLevelNodeSelector[ - CHAIN_ID types.ID, - RPC any, -] struct { - nodes []Node[CHAIN_ID, RPC] - roundRobinCount []atomic.Uint32 -} - -type nodeWithPriority[ - CHAIN_ID types.ID, - RPC any, -] struct { - node Node[CHAIN_ID, RPC] - priority int32 -} - -func NewPriorityLevelNodeSelector[ - CHAIN_ID types.ID, - RPC any, -](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { - return &priorityLevelNodeSelector[CHAIN_ID, RPC]{ - nodes: nodes, - roundRobinCount: make([]atomic.Uint32, nrOfPriorityTiers(nodes)), - } -} - -func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, 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, 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, RPC]) getHighestPriorityAliveTier() []nodeWithPriority[CHAIN_ID, RPC] { - var nodes []nodeWithPriority[CHAIN_ID, RPC] - for _, n := range s.nodes { - if n.State() == nodeStateAlive { - nodes = append(nodes, nodeWithPriority[CHAIN_ID, 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, - RPC any, -](nodes []nodeWithPriority[CHAIN_ID, RPC]) []nodeWithPriority[CHAIN_ID, RPC] { - sort.SliceStable(nodes, func(i, j int) bool { - return nodes[i].priority > nodes[j].priority - }) - - var nodes2 []nodeWithPriority[CHAIN_ID, 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, - RPC any, -](nodes []Node[CHAIN_ID, 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, - RPC any, -](nodes []Node[CHAIN_ID, RPC]) Node[CHAIN_ID, RPC] { - hp := int32(math.MaxInt32) - var node Node[CHAIN_ID, RPC] - for _, n := range nodes { - if n.Order() < hp { - hp = n.Order() - node = n - } - } - return node -} diff --git a/common/client/node_selector_priority_level_test.go b/common/client/node_selector_priority_level_test.go deleted file mode 100644 index 67aac97be1b..00000000000 --- a/common/client/node_selector_priority_level_test.go +++ /dev/null @@ -1,91 +0,0 @@ -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, RPCClient[types.ID, Head]](NodeSelectionModePriorityLevel, nil) - assert.Equal(t, selector.Name(), NodeSelectionModePriorityLevel) -} - -func TestPriorityLevelNodeSelector(t *testing.T) { - t.Parallel() - - type nodeClient RPCClient[types.ID, Head] - type testNode struct { - order int32 - state nodeState - } - type testCase struct { - name string - nodes []testNode - expect []int // indexes of the nodes expected to be returned by Select - } - - testCases := []testCase{ - { - name: "TwoNodesSameOrder: Highest Allowed Order", - nodes: []testNode{ - {order: 1, state: nodeStateAlive}, - {order: 1, state: nodeStateAlive}, - }, - expect: []int{0, 1, 0, 1, 0, 1}, - }, - { - name: "TwoNodesSameOrder: Lowest Allowed Order", - nodes: []testNode{ - {order: 100, state: nodeStateAlive}, - {order: 100, state: nodeStateAlive}, - }, - expect: []int{0, 1, 0, 1, 0, 1}, - }, - { - name: "NoneAvailable", - nodes: []testNode{ - {order: 1, state: nodeStateOutOfSync}, - {order: 1, state: nodeStateUnreachable}, - {order: 1, state: nodeStateUnreachable}, - }, - expect: []int{}, // no nodes should be selected - }, - { - name: "DifferentOrder", - nodes: []testNode{ - {order: 1, state: nodeStateAlive}, - {order: 2, state: nodeStateAlive}, - {order: 3, state: nodeStateAlive}, - }, - expect: []int{0, 0}, // only the highest order node should be selected - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var nodes []Node[types.ID, nodeClient] - for _, tn := range tc.nodes { - node := newMockNode[types.ID, nodeClient](t) - node.On("State").Return(tn.state) - node.On("Order").Return(tn.order) - nodes = append(nodes, node) - } - - selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes) - for _, idx := range tc.expect { - if idx >= len(nodes) { - t.Fatalf("Invalid node index %d in test case '%s'", idx, tc.name) - } - assert.Same(t, nodes[idx], selector.Select()) - } - - // Check for nil selection if expected slice is empty - if len(tc.expect) == 0 { - assert.Nil(t, selector.Select()) - } - }) - } -} diff --git a/common/client/node_selector_round_robin.go b/common/client/node_selector_round_robin.go deleted file mode 100644 index 18cea03ebd5..00000000000 --- a/common/client/node_selector_round_robin.go +++ /dev/null @@ -1,48 +0,0 @@ -package client - -import ( - "sync/atomic" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -type roundRobinSelector[ - CHAIN_ID types.ID, - RPC any, -] struct { - nodes []Node[CHAIN_ID, RPC] - roundRobinCount atomic.Uint32 -} - -func NewRoundRobinSelector[ - CHAIN_ID types.ID, - RPC any, -](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { - return &roundRobinSelector[CHAIN_ID, RPC]{ - nodes: nodes, - } -} - -func (s *roundRobinSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { - var liveNodes []Node[CHAIN_ID, 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, RPC]) Name() string { - return NodeSelectionModeRoundRobin -} diff --git a/common/client/node_selector_round_robin_test.go b/common/client/node_selector_round_robin_test.go deleted file mode 100644 index 189b58da9ea..00000000000 --- a/common/client/node_selector_round_robin_test.go +++ /dev/null @@ -1,61 +0,0 @@ -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, RPCClient[types.ID, Head]](NodeSelectionModeRoundRobin, nil) - assert.Equal(t, selector.Name(), NodeSelectionModeRoundRobin) -} - -func TestRoundRobinNodeSelector(t *testing.T) { - t.Parallel() - - type nodeClient RPCClient[types.ID, Head] - var nodes []Node[types.ID, nodeClient] - - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, 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 RPCClient[types.ID, Head] - var nodes []Node[types.ID, nodeClient] - - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, 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 deleted file mode 100644 index f652bfc50ad..00000000000 --- a/common/client/node_selector_test.go +++ /dev/null @@ -1,18 +0,0 @@ -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, RPCClient[types.ID, Head]]("unknown", nil) - }) - }) -} diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go deleted file mode 100644 index 3a4b9733f0d..00000000000 --- a/common/client/node_selector_total_difficulty.go +++ /dev/null @@ -1,53 +0,0 @@ -package client - -import ( - "math/big" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -type totalDifficultyNodeSelector[ - CHAIN_ID types.ID, - RPC any, -] []Node[CHAIN_ID, RPC] - -func NewTotalDifficultyNodeSelector[ - CHAIN_ID types.ID, - RPC any, -](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { - return totalDifficultyNodeSelector[CHAIN_ID, RPC](nodes) -} - -func (s totalDifficultyNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { - // NodeNoNewHeadsThreshold may not be enabled, in this case all nodes have td == nil - var highestTD *big.Int - var nodes []Node[CHAIN_ID, RPC] - var aliveNodes []Node[CHAIN_ID, RPC] - - for _, n := range s { - state, currentChainInfo := n.StateAndLatest() - if state != nodeStateAlive { - continue - } - - currentTD := currentChainInfo.TotalDifficulty - 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, RPC]) Name() string { - return NodeSelectionModeTotalDifficulty -} diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go deleted file mode 100644 index 7cd5867ddca..00000000000 --- a/common/client/node_selector_total_difficulty_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package client - -import ( - "math/big" - "testing" - - "github.com/smartcontractkit/chainlink/v2/common/types" - - "github.com/stretchr/testify/assert" -) - -func TestTotalDifficultyNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeTotalDifficulty, nil) - assert.Equal(t, selector.Name(), NodeSelectionModeTotalDifficulty) -} - -func TestTotalDifficultyNodeSelector(t *testing.T) { - t.Parallel() - - type nodeClient RPCClient[types.ID, Head] - var nodes []Node[types.ID, nodeClient] - - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, nodeClient](t) - if i == 0 { - // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1}) - } else if i == 1 { - // second node is alive - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) - } else { - // third node is alive and best - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(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, nodeClient](t) - // fourth node is alive (same as 3rd) - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(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, nodeClient](t) - // fifth node is alive (better than 3rd and 4th) - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(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, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) - node1.On("Order").Maybe().Return(int32(1)) - node2 := newMockNode[types.ID, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) - node2.On("Order").Maybe().Return(int32(1)) - nodes := []Node[types.ID, nodeClient]{node1, node2} - - selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) - assert.Same(t, node1, selector.Select()) - }) -} - -func TestTotalDifficultyNodeSelector_None(t *testing.T) { - t.Parallel() - - type nodeClient RPCClient[types.ID, Head] - var nodes []Node[types.ID, nodeClient] - - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, nodeClient](t) - if i == 0 { - // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) - } else { - // others are unreachable - node.On("StateAndLatest").Return(nodeStateUnreachable, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) - } - nodes = append(nodes, node) - } - - selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) - assert.Nil(t, selector.Select()) -} - -func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { - t.Parallel() - - type nodeClient RPCClient[types.ID, Head] - var nodes []Node[types.ID, nodeClient] - - t.Run("same td and order", func(t *testing.T) { - for i := 0; i < 3; i++ { - node := newMockNode[types.ID, nodeClient](t) - node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(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, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) - node1.On("Order").Return(int32(3)) - - node2 := newMockNode[types.ID, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) - node2.On("Order").Return(int32(1)) - - node3 := newMockNode[types.ID, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) - node3.On("Order").Return(int32(2)) - - nodes := []Node[types.ID, 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, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) - node1.On("Order").Maybe().Return(int32(3)) - - node2 := newMockNode[types.ID, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(11)}) - node2.On("Order").Maybe().Return(int32(3)) - - node3 := newMockNode[types.ID, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(12)}) - node3.On("Order").Return(int32(3)) - - nodes := []Node[types.ID, 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, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(100)}) - node1.On("Order").Maybe().Return(int32(4)) - - node2 := newMockNode[types.ID, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) - node2.On("Order").Maybe().Return(int32(5)) - - node3 := newMockNode[types.ID, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) - node3.On("Order").Maybe().Return(int32(1)) - - node4 := newMockNode[types.ID, nodeClient](t) - node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(105)}) - node4.On("Order").Maybe().Return(int32(2)) - - nodes := []Node[types.ID, 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 deleted file mode 100644 index 6d98c2d9ea4..00000000000 --- a/common/client/node_test.go +++ /dev/null @@ -1,107 +0,0 @@ -package client - -import ( - "net/url" - "testing" - "time" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - - clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks" - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -type testNodeConfig struct { - pollFailureThreshold uint32 - pollInterval time.Duration - selectionMode string - syncThreshold uint32 - nodeIsSyncingEnabled bool - enforceRepeatableRead bool - finalizedBlockPollInterval time.Duration - deathDeclarationDelay time.Duration - newHeadsPollInterval time.Duration -} - -func (n testNodeConfig) NewHeadsPollInterval() time.Duration { - return n.newHeadsPollInterval -} - -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 -} - -func (n testNodeConfig) NodeIsSyncingEnabled() bool { - return n.nodeIsSyncingEnabled -} - -func (n testNodeConfig) FinalizedBlockPollInterval() time.Duration { - return n.finalizedBlockPollInterval -} - -func (n testNodeConfig) EnforceRepeatableRead() bool { - return n.enforceRepeatableRead -} - -func (n testNodeConfig) DeathDeclarationDelay() time.Duration { - return n.deathDeclarationDelay -} - -type testNode struct { - *node[types.ID, Head, RPCClient[types.ID, Head]] -} - -type testNodeOpts struct { - config testNodeConfig - chainConfig clientMocks.ChainConfig - lggr logger.Logger - wsuri *url.URL - httpuri *url.URL - name string - id int - chainID types.ID - nodeOrder int32 - rpc *mockRPCClient[types.ID, Head] - chainFamily string -} - -func newTestNode(t *testing.T, opts testNodeOpts) testNode { - if opts.lggr == nil { - opts.lggr = logger.Test(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, RPCClient[types.ID, Head]](opts.config, opts.chainConfig, 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, RPCClient[types.ID, Head]]), - } -} diff --git a/common/client/poller.go b/common/client/poller.go deleted file mode 100644 index bdd3d36f16f..00000000000 --- a/common/client/poller.go +++ /dev/null @@ -1,95 +0,0 @@ -package client - -import ( - "context" - "time" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -// Poller is a component that polls a function at a given interval -// and delivers the result to a channel. It is used by multinode to poll -// for new heads and implements the Subscription interface. -type Poller[T any] struct { - services.Service - eng *services.Engine - - pollingInterval time.Duration - pollingFunc func(ctx context.Context) (T, error) - pollingTimeout time.Duration - channel chan<- T - errCh chan error -} - -// NewPoller creates a new Poller instance and returns a channel to receive the polled data -func NewPoller[ - T any, -](pollingInterval time.Duration, pollingFunc func(ctx context.Context) (T, error), pollingTimeout time.Duration, lggr logger.Logger) (Poller[T], <-chan T) { - channel := make(chan T) - p := Poller[T]{ - pollingInterval: pollingInterval, - pollingFunc: pollingFunc, - pollingTimeout: pollingTimeout, - channel: channel, - errCh: make(chan error), - } - p.Service, p.eng = services.Config{ - Name: "Poller", - Start: p.start, - Close: p.close, - }.NewServiceEngine(lggr) - return p, channel -} - -var _ types.Subscription = &Poller[any]{} - -func (p *Poller[T]) start(ctx context.Context) error { - p.eng.Go(p.pollingLoop) - return nil -} - -// Unsubscribe cancels the sending of events to the data channel -func (p *Poller[T]) Unsubscribe() { - _ = p.Close() -} - -func (p *Poller[T]) close() error { - close(p.errCh) - close(p.channel) - return nil -} - -func (p *Poller[T]) Err() <-chan error { - return p.errCh -} - -func (p *Poller[T]) pollingLoop(ctx context.Context) { - ticker := services.NewTicker(p.pollingInterval) // reduce possibility of sending two exactly the same request at once - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - // Set polling timeout - pollingCtx, cancelPolling := context.WithTimeout(ctx, p.pollingTimeout) - // Execute polling function - result, err := p.pollingFunc(pollingCtx) - cancelPolling() - if err != nil { - p.eng.Warnf("polling error: %v", err) - continue - } - // Send result to channel or block if channel is full - select { - case p.channel <- result: - case <-ctx.Done(): - return - } - } - } -} diff --git a/common/client/poller_test.go b/common/client/poller_test.go deleted file mode 100644 index 930b101751c..00000000000 --- a/common/client/poller_test.go +++ /dev/null @@ -1,194 +0,0 @@ -package client - -import ( - "context" - "fmt" - "math/big" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" -) - -func Test_Poller(t *testing.T) { - lggr := logger.Test(t) - - t.Run("Test multiple start", func(t *testing.T) { - ctx := tests.Context(t) - pollFunc := func(ctx context.Context) (Head, error) { - return nil, nil - } - - poller, _ := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr) - err := poller.Start(ctx) - require.NoError(t, err) - - err = poller.Start(ctx) - require.Error(t, err) - poller.Unsubscribe() - }) - - t.Run("Test polling for heads", func(t *testing.T) { - ctx := tests.Context(t) - // Mock polling function that returns a new value every time it's called - var pollNumber int - pollLock := sync.Mutex{} - pollFunc := func(ctx context.Context) (Head, error) { - pollLock.Lock() - defer pollLock.Unlock() - pollNumber++ - h := head{ - BlockNumber: int64(pollNumber), - BlockDifficulty: big.NewInt(int64(pollNumber)), - } - return h.ToMockHead(t), nil - } - - // Create poller and start to receive data - poller, channel := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr) - require.NoError(t, poller.Start(ctx)) - defer poller.Unsubscribe() - - // Receive updates from the poller - pollCount := 0 - pollMax := 50 - for ; pollCount < pollMax; pollCount++ { - h := <-channel - assert.Equal(t, int64(pollCount+1), h.BlockNumber()) - } - }) - - t.Run("Test polling errors", func(t *testing.T) { - ctx := tests.Context(t) - // Mock polling function that returns an error - var pollNumber int - pollLock := sync.Mutex{} - pollFunc := func(ctx context.Context) (Head, error) { - pollLock.Lock() - defer pollLock.Unlock() - pollNumber++ - return nil, fmt.Errorf("polling error %d", pollNumber) - } - - olggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) - - // Create poller and subscribe to receive data - poller, _ := NewPoller[Head](time.Millisecond, pollFunc, time.Second, olggr) - require.NoError(t, poller.Start(ctx)) - defer poller.Unsubscribe() - - // Ensure that all errors were logged as expected - logsSeen := func() bool { - for pollCount := 0; pollCount < 50; pollCount++ { - numLogs := observedLogs.FilterMessage(fmt.Sprintf("polling error: polling error %d", pollCount+1)).Len() - if numLogs != 1 { - return false - } - } - return true - } - require.Eventually(t, logsSeen, tests.WaitTimeout(t), 100*time.Millisecond) - }) - - t.Run("Test polling timeout", func(t *testing.T) { - ctx := tests.Context(t) - pollFunc := func(ctx context.Context) (Head, error) { - if <-ctx.Done(); true { - return nil, ctx.Err() - } - return nil, nil - } - - // Set instant timeout - pollingTimeout := time.Duration(0) - - olggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) - - // Create poller and subscribe to receive data - poller, _ := NewPoller[Head](time.Millisecond, pollFunc, pollingTimeout, olggr) - require.NoError(t, poller.Start(ctx)) - defer poller.Unsubscribe() - - // Ensure that timeout errors were logged as expected - logsSeen := func() bool { - return observedLogs.FilterMessage("polling error: context deadline exceeded").Len() >= 1 - } - require.Eventually(t, logsSeen, tests.WaitTimeout(t), 100*time.Millisecond) - }) - - t.Run("Test unsubscribe during polling", func(t *testing.T) { - ctx := tests.Context(t) - wait := make(chan struct{}) - closeOnce := sync.OnceFunc(func() { close(wait) }) - pollFunc := func(ctx context.Context) (Head, error) { - closeOnce() - // Block in polling function until context is cancelled - if <-ctx.Done(); true { - return nil, ctx.Err() - } - return nil, nil - } - - // Set long timeout - pollingTimeout := time.Minute - - olggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) - - // Create poller and subscribe to receive data - poller, _ := NewPoller[Head](time.Millisecond, pollFunc, pollingTimeout, olggr) - require.NoError(t, poller.Start(ctx)) - - // Unsubscribe while blocked in polling function - <-wait - poller.Unsubscribe() - - // Ensure error was logged - logsSeen := func() bool { - return observedLogs.FilterMessage("polling error: context canceled").Len() >= 1 - } - require.Eventually(t, logsSeen, tests.WaitTimeout(t), 100*time.Millisecond) - }) -} - -func Test_Poller_Unsubscribe(t *testing.T) { - lggr := logger.Test(t) - pollFunc := func(ctx context.Context) (Head, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - h := head{ - BlockNumber: 0, - BlockDifficulty: big.NewInt(0), - } - return h.ToMockHead(t), nil - } - } - - t.Run("Test multiple unsubscribe", func(t *testing.T) { - ctx := tests.Context(t) - poller, channel := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr) - err := poller.Start(ctx) - require.NoError(t, err) - - <-channel - poller.Unsubscribe() - poller.Unsubscribe() - }) - - t.Run("Read channel after unsubscribe", func(t *testing.T) { - ctx := tests.Context(t) - poller, channel := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr) - err := poller.Start(ctx) - require.NoError(t, err) - - poller.Unsubscribe() - require.Equal(t, <-channel, nil) - }) -} diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go deleted file mode 100644 index 0a1715fa191..00000000000 --- a/common/client/send_only_node.go +++ /dev/null @@ -1,183 +0,0 @@ -package client - -import ( - "context" - "fmt" - "net/url" - "sync" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -type sendOnlyClient[ - CHAIN_ID types.ID, -] interface { - Close() - ChainID(context.Context) (CHAIN_ID, error) - Dial(ctx context.Context) error -} - -// SendOnlyNode represents one node used as a sendonly -type SendOnlyNode[ - CHAIN_ID types.ID, - RPC any, -] 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 services.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 = logger.Named(logger.Named(lggr, "SendOnlyNode"), name) - s.log = logger.With(s.log, - "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.Dial(startCtx) - 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() - close(s.chStop) - 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", 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 deleted file mode 100644 index c66d267ed42..00000000000 --- a/common/client/send_only_node_lifecycle.go +++ /dev/null @@ -1,67 +0,0 @@ -package client - -import ( - "fmt" - "time" - - "github.com/smartcontractkit/chainlink/v2/common/internal/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() - ctx, cancel := s.chStop.NewCtx() - defer cancel() - - backoff := utils.NewRedialBackoff() - for { - select { - case <-ctx.Done(): - return - case <-time.After(backoff.Duration()): - } - chainID, err := s.rpc.ChainID(ctx) - 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/send_only_node_test.go b/common/client/send_only_node_test.go deleted file mode 100644 index 532946da48f..00000000000 --- a/common/client/send_only_node_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package client - -import ( - "errors" - "fmt" - "net/url" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "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" -) - -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.Test(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.TestObserved(t, zap.WarnLevel) - client := newMockSendOnlyClient[types.ID](t) - client.On("Close").Once() - expectedError := errors.New("some http error") - client.On("Dial", mock.Anything).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.TestObserved(t, zap.WarnLevel) - client := newMockSendOnlyClient[types.ID](t) - client.On("Close").Once() - client.On("Dial", mock.Anything).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.TestObserved(t, zap.WarnLevel) - client := newMockSendOnlyClient[types.ID](t) - client.On("Close").Once() - client.On("Dial", mock.Anything).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.TestObserved(t, zap.WarnLevel) - client := newMockSendOnlyClient[types.ID](t) - client.On("Close").Once() - client.On("Dial", mock.Anything).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.TestObserved(t, zap.WarnLevel) - client := newMockSendOnlyClient[types.ID](t) - client.On("Close").Once() - client.On("Dial", mock.Anything).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/timeout.go b/common/client/timeout.go new file mode 100644 index 00000000000..0827b812962 --- /dev/null +++ b/common/client/timeout.go @@ -0,0 +1,5 @@ +package client + +import "time" + +const QueryTimeout = 10 * time.Second diff --git a/common/client/transaction_sender.go b/common/client/transaction_sender.go deleted file mode 100644 index 5f58682142f..00000000000 --- a/common/client/transaction_sender.go +++ /dev/null @@ -1,301 +0,0 @@ -package client - -import ( - "context" - "errors" - "math" - "slices" - "sync" - "time" - - "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" -) - -var ( - // PromMultiNodeInvariantViolations reports violation of our assumptions - PromMultiNodeInvariantViolations = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "multi_node_invariant_violations", - Help: "The number of invariant violations", - }, []string{"network", "chainId", "invariant"}) -) - -type SendTxResult interface { - Code() SendTxReturnCode - Error() error -} - -const sendTxQuorum = 0.7 - -// SendTxRPCClient - defines interface of an RPC used by TransactionSender to broadcast transaction -type SendTxRPCClient[TX any, RESULT SendTxResult] interface { - // SendTransaction errors returned should include name or other unique identifier of the RPC - SendTransaction(ctx context.Context, tx TX) RESULT -} - -func NewTransactionSender[TX any, RESULT SendTxResult, CHAIN_ID types.ID, RPC SendTxRPCClient[TX, RESULT]]( - lggr logger.Logger, - chainID CHAIN_ID, - chainFamily string, - multiNode *MultiNode[CHAIN_ID, RPC], - newResult func(err error) RESULT, - sendTxSoftTimeout time.Duration, -) *TransactionSender[TX, RESULT, CHAIN_ID, RPC] { - if sendTxSoftTimeout == 0 { - sendTxSoftTimeout = QueryTimeout / 2 - } - return &TransactionSender[TX, RESULT, CHAIN_ID, RPC]{ - chainID: chainID, - chainFamily: chainFamily, - lggr: logger.Sugared(lggr).Named("TransactionSender").With("chainID", chainID.String()), - multiNode: multiNode, - newResult: newResult, - sendTxSoftTimeout: sendTxSoftTimeout, - chStop: make(services.StopChan), - } -} - -type TransactionSender[TX any, RESULT SendTxResult, CHAIN_ID types.ID, RPC SendTxRPCClient[TX, RESULT]] struct { - services.StateMachine - chainID CHAIN_ID - chainFamily string - lggr logger.SugaredLogger - multiNode *MultiNode[CHAIN_ID, RPC] - newResult func(err error) RESULT - sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation - - wg sync.WaitGroup // waits for all reporting goroutines to finish - chStop services.StopChan -} - -// SendTransaction - broadcasts transaction to all the send-only and primary nodes in MultiNode. -// A returned nil or error does not guarantee that the transaction will or won't be included. Additional checks must be -// performed to determine the final state. -// -// Send-only nodes' results are ignored as they tend to return false-positive responses. Broadcast to them is necessary -// to speed up the propagation of TX in the network. -// -// Handling of primary nodes' results consists of collection and aggregation. -// In the collection step, we gather as many results as possible while minimizing waiting time. This operation succeeds -// on one of the following conditions: -// * Received at least one success -// * Received at least one result and `sendTxSoftTimeout` expired -// * Received results from the sufficient number of nodes defined by sendTxQuorum. -// The aggregation is based on the following conditions: -// * If there is at least one success - returns success -// * If there is at least one terminal error - returns terminal error -// * If there is both success and terminal error - returns success and reports invariant violation -// * Otherwise, returns any (effectively random) of the errors. -func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) SendTransaction(ctx context.Context, tx TX) RESULT { - var result RESULT - ctx, cancel := txSender.chStop.Ctx(ctx) - defer cancel() - if !txSender.IfStarted(func() { - txResults := make(chan RESULT) - txResultsToReport := make(chan RESULT) - primaryNodeWg := sync.WaitGroup{} - - healthyNodesNum := 0 - err := txSender.multiNode.DoAll(ctx, func(ctx context.Context, rpc RPC, isSendOnly bool) { - if isSendOnly { - txSender.wg.Add(1) - go func(ctx context.Context) { - defer txSender.wg.Done() - // Send-only nodes' results are ignored as they tend to return false-positive responses. - // Broadcast to them is necessary to speed up the propagation of TX in the network. - _ = txSender.broadcastTxAsync(ctx, rpc, tx) - }(ctx) - return - } - - // Primary Nodes - healthyNodesNum++ - primaryNodeWg.Add(1) - go func(ctx context.Context) { - // Broadcasting transaction and results reporting for invariant detection are background jobs that must be detached from - // callers cancellation. - // Results reporting to SendTransaction caller must respect caller's context to avoid goroutine leak. - defer primaryNodeWg.Done() - r := txSender.broadcastTxAsync(ctx, rpc, tx) - select { - case <-ctx.Done(): - txSender.lggr.Debugw("Failed to send tx results", "err", ctx.Err()) - return - case txResults <- r: - } - - ctx, cancel := txSender.chStop.Ctx(context.WithoutCancel(ctx)) - defer cancel() - select { - case <-ctx.Done(): - txSender.lggr.Debugw("Failed to send tx results to report", "err", ctx.Err()) - return - case txResultsToReport <- r: - } - }(ctx) - }) - - // This needs to be done in parallel so the reporting knows when it's done (when the channel is closed) - txSender.wg.Add(1) - go func() { - defer txSender.wg.Done() - primaryNodeWg.Wait() - close(txResultsToReport) - close(txResults) - }() - - if err != nil { - result = txSender.newResult(err) - return - } - - if healthyNodesNum == 0 { - result = txSender.newResult(ErroringNodeError) - return - } - - txSender.wg.Add(1) - go txSender.reportSendTxAnomalies(tx, txResultsToReport) - - result = txSender.collectTxResults(ctx, tx, healthyNodesNum, txResults) - }) { - result = txSender.newResult(errors.New("TransactionSender not started")) - } - - return result -} - -func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) broadcastTxAsync(ctx context.Context, rpc RPC, tx TX) RESULT { - // broadcast is a background job, so always detach from caller's cancellation - ctx, cancel := txSender.chStop.Ctx(context.WithoutCancel(ctx)) - defer cancel() - result := rpc.SendTransaction(ctx, tx) - txSender.lggr.Debugw("Node sent transaction", "tx", tx, "err", result.Error()) - if !slices.Contains(sendTxSuccessfulCodes, result.Code()) && ctx.Err() == nil { - txSender.lggr.Warnw("RPC returned error", "tx", tx, "err", result.Error()) - } - return result -} - -func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) reportSendTxAnomalies(tx TX, txResults <-chan RESULT) { - defer txSender.wg.Done() - resultsByCode := sendTxResults[RESULT]{} - // txResults eventually will be closed - for txResult := range txResults { - resultsByCode[txResult.Code()] = append(resultsByCode[txResult.Code()], txResult) - } - - select { - case <-txSender.chStop: - // it's ok to receive no results if txSender is closing. Return early to prevent false reporting of invariant violation. - if len(resultsByCode) == 0 { - return - } - default: - } - - _, criticalErr := aggregateTxResults[RESULT](resultsByCode) - if criticalErr != nil { - txSender.lggr.Criticalw("observed invariant violation on SendTransaction", "tx", tx, "resultsByCode", resultsByCode, "err", criticalErr) - PromMultiNodeInvariantViolations.WithLabelValues(txSender.chainFamily, txSender.chainID.String(), criticalErr.Error()).Inc() - } -} - -type sendTxResults[RESULT any] map[SendTxReturnCode][]RESULT - -func aggregateTxResults[RESULT any](resultsByCode sendTxResults[RESULT]) (result RESULT, criticalErr error) { - severeErrors, hasSevereErrors := findFirstIn(resultsByCode, sendTxSevereErrors) - successResults, hasSuccess := findFirstIn(resultsByCode, sendTxSuccessfulCodes) - if hasSuccess { - // We assume that primary node would never report false positive txResult for a transaction. - // Thus, if such case occurs it's probably due to misconfiguration or a bug and requires manual intervention. - if hasSevereErrors { - const errMsg = "found contradictions in nodes replies on SendTransaction: got success and severe error" - // return success, since at least 1 node has accepted our broadcasted Tx, and thus it can now be included onchain - return successResults[0], errors.New(errMsg) - } - - // other errors are temporary - we are safe to return success - return successResults[0], nil - } - - if hasSevereErrors { - return severeErrors[0], nil - } - - // return temporary error - for _, r := range resultsByCode { - return r[0], nil - } - - criticalErr = errors.New("expected at least one response on SendTransaction") - return result, criticalErr -} - -func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan RESULT) RESULT { - requiredResults := int(math.Ceil(float64(healthyNodesNum) * sendTxQuorum)) - errorsByCode := sendTxResults[RESULT]{} - var softTimeoutChan <-chan time.Time - var resultsCount int -loop: - for { - select { - case <-ctx.Done(): - txSender.lggr.Debugw("Failed to collect of the results before context was done", "tx", tx, "errorsByCode", errorsByCode) - return txSender.newResult(ctx.Err()) - case r := <-txResults: - errorsByCode[r.Code()] = append(errorsByCode[r.Code()], r) - resultsCount++ - if slices.Contains(sendTxSuccessfulCodes, r.Code()) || resultsCount >= requiredResults { - break loop - } - case <-softTimeoutChan: - txSender.lggr.Debugw("Send Tx soft timeout expired - returning responses we've collected so far", "tx", tx, "resultsCount", resultsCount, "requiredResults", requiredResults) - break loop - } - - if softTimeoutChan == nil { - tm := time.NewTimer(txSender.sendTxSoftTimeout) - softTimeoutChan = tm.C - // we are fine with stopping timer at the end of function - //nolint - defer tm.Stop() - } - } - - // ignore critical error as it's reported in reportSendTxAnomalies - result, _ := aggregateTxResults(errorsByCode) - txSender.lggr.Debugw("Collected results", "errorsByCode", errorsByCode, "result", result) - return result -} - -func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) Start(ctx context.Context) error { - return txSender.StartOnce("TransactionSender", func() error { - return nil - }) -} - -func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) Close() error { - return txSender.StopOnce("TransactionSender", func() error { - txSender.lggr.Debug("Closing TransactionSender") - close(txSender.chStop) - txSender.wg.Wait() - return nil - }) -} - -// findFirstIn - returns the first existing key and value for the slice of keys -func findFirstIn[K comparable, V any](set map[K]V, keys []K) (V, bool) { - for _, k := range keys { - if v, ok := set[k]; ok { - return v, true - } - } - var zeroV V - return zeroV, false -} diff --git a/common/client/transaction_sender_test.go b/common/client/transaction_sender_test.go deleted file mode 100644 index 14dc0799a6a..00000000000 --- a/common/client/transaction_sender_test.go +++ /dev/null @@ -1,419 +0,0 @@ -package client - -import ( - "context" - "fmt" - "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-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -type TestSendTxRPCClient SendTxRPCClient[any, *sendTxResult] - -type sendTxMultiNode struct { - *MultiNode[types.ID, TestSendTxRPCClient] -} - -type sendTxRPC struct { - sendTxRun func(args mock.Arguments) - sendTxErr error -} - -type sendTxResult struct { - err error - code SendTxReturnCode -} - -var _ SendTxResult = (*sendTxResult)(nil) - -func NewSendTxResult(err error) *sendTxResult { - result := &sendTxResult{ - err: err, - } - return result -} - -func (r *sendTxResult) Error() error { - return r.err -} - -func (r *sendTxResult) Code() SendTxReturnCode { - return r.code -} - -var _ TestSendTxRPCClient = (*sendTxRPC)(nil) - -func newSendTxRPC(sendTxErr error, sendTxRun func(args mock.Arguments)) *sendTxRPC { - return &sendTxRPC{sendTxErr: sendTxErr, sendTxRun: sendTxRun} -} - -func (rpc *sendTxRPC) SendTransaction(ctx context.Context, _ any) *sendTxResult { - if rpc.sendTxRun != nil { - rpc.sendTxRun(mock.Arguments{ctx}) - } - return &sendTxResult{err: rpc.sendTxErr, code: classifySendTxError(nil, rpc.sendTxErr)} -} - -// newTestTransactionSender returns a sendTxMultiNode and TransactionSender. -// Only the TransactionSender is run via Start/Close. -func newTestTransactionSender(t *testing.T, chainID types.ID, lggr logger.Logger, - nodes []Node[types.ID, TestSendTxRPCClient], - sendOnlyNodes []SendOnlyNode[types.ID, TestSendTxRPCClient], -) (*sendTxMultiNode, *TransactionSender[any, *sendTxResult, types.ID, TestSendTxRPCClient]) { - mn := sendTxMultiNode{NewMultiNode[types.ID, TestSendTxRPCClient]( - lggr, NodeSelectionModeRoundRobin, 0, nodes, sendOnlyNodes, chainID, "chainFamily", 0)} - - txSender := NewTransactionSender[any, *sendTxResult, types.ID, TestSendTxRPCClient](lggr, chainID, mn.chainFamily, mn.MultiNode, NewSendTxResult, tests.TestInterval) - servicetest.Run(t, txSender) - return &mn, txSender -} - -func classifySendTxError(_ any, err error) SendTxReturnCode { - if err != nil { - return Fatal - } - return Successful -} - -func TestTransactionSender_SendTransaction(t *testing.T) { - t.Parallel() - - newNodeWithState := func(t *testing.T, state nodeState, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, TestSendTxRPCClient] { - rpc := newSendTxRPC(txErr, sendTxRun) - node := newMockNode[types.ID, TestSendTxRPCClient](t) - node.On("String").Return("node name").Maybe() - node.On("RPC").Return(rpc).Maybe() - node.On("State").Return(state).Maybe() - node.On("Start", mock.Anything).Return(nil).Maybe() - node.On("Close").Return(nil).Maybe() - node.On("SetPoolChainInfoProvider", mock.Anything).Return(nil).Maybe() - return node - } - - newNode := func(t *testing.T, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, TestSendTxRPCClient] { - return newNodeWithState(t, nodeStateAlive, txErr, sendTxRun) - } - - t.Run("Fails if there is no nodes available", func(t *testing.T) { - lggr := logger.Test(t) - _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, nil, nil) - result := txSender.SendTransaction(tests.Context(t), nil) - assert.EqualError(t, result.Error(), ErroringNodeError.Error()) - }) - - t.Run("Transaction failure happy path", func(t *testing.T) { - expectedError := errors.New("transaction failed") - mainNode := newNode(t, expectedError, nil) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - - _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, - []Node[types.ID, TestSendTxRPCClient]{mainNode}, - []SendOnlyNode[types.ID, TestSendTxRPCClient]{newNode(t, errors.New("unexpected error"), nil)}) - - result := txSender.SendTransaction(tests.Context(t), nil) - require.ErrorIs(t, result.Error(), expectedError) - require.Equal(t, Fatal, result.Code()) - tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) - tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 2) - }) - - t.Run("Transaction success happy path", func(t *testing.T) { - mainNode := newNode(t, nil, nil) - - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, - []Node[types.ID, TestSendTxRPCClient]{mainNode}, - []SendOnlyNode[types.ID, TestSendTxRPCClient]{newNode(t, errors.New("unexpected error"), nil)}) - - result := txSender.SendTransaction(tests.Context(t), nil) - require.NoError(t, result.Error()) - require.Equal(t, Successful, result.Code()) - tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) - tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 1) - }) - - t.Run("Context expired before collecting sufficient results", func(t *testing.T) { - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - - mainNode := newNode(t, nil, func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - - lggr := logger.Test(t) - - _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, - []Node[types.ID, TestSendTxRPCClient]{mainNode}, nil) - - requestContext, cancel := context.WithCancel(tests.Context(t)) - cancel() - result := txSender.SendTransaction(requestContext, nil) - require.EqualError(t, result.Error(), "context canceled") - }) - - t.Run("Soft timeout stops results collection", func(t *testing.T) { - chainID := types.RandomID() - expectedError := errors.New("transaction failed") - fastNode := newNode(t, expectedError, nil) - - // hold reply from the node till end of the test - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - - lggr := logger.Test(t) - - _, txSender := newTestTransactionSender(t, chainID, lggr, []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode}, nil) - result := txSender.SendTransaction(tests.Context(t), nil) - require.EqualError(t, result.Error(), expectedError.Error()) - }) - t.Run("Returns success without waiting for the rest of the nodes", func(t *testing.T) { - chainID := types.RandomID() - fastNode := newNode(t, nil, nil) - // hold reply from the node till end of the test - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - lggr, _ := logger.TestObserved(t, zap.WarnLevel) - _, txSender := newTestTransactionSender(t, chainID, lggr, - []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode}, - []SendOnlyNode[types.ID, TestSendTxRPCClient]{slowSendOnly}) - - result := txSender.SendTransaction(tests.Context(t), nil) - require.NoError(t, result.Error()) - require.Equal(t, Successful, result.Code()) - }) - t.Run("Fails when multinode is closed", func(t *testing.T) { - chainID := types.RandomID() - fastNode := newNode(t, nil, nil) - fastNode.On("ConfiguredChainID").Return(chainID).Maybe() - // hold reply from the node till end of the test - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - slowNode.On("ConfiguredChainID").Return(chainID).Maybe() - slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - slowSendOnly.On("ConfiguredChainID").Return(chainID).Maybe() - - lggr, _ := logger.TestObserved(t, zap.DebugLevel) - - mn, txSender := newTestTransactionSender(t, chainID, lggr, - []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode}, - []SendOnlyNode[types.ID, TestSendTxRPCClient]{slowSendOnly}) - - require.NoError(t, mn.Start(tests.Context(t))) - require.NoError(t, mn.Close()) - result := txSender.SendTransaction(tests.Context(t), nil) - require.EqualError(t, result.Error(), "service is stopped") - }) - t.Run("Fails when closed", func(t *testing.T) { - chainID := types.RandomID() - fastNode := newNode(t, nil, nil) - // hold reply from the node till end of the test - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - - var txSender *TransactionSender[any, *sendTxResult, types.ID, TestSendTxRPCClient] - - t.Cleanup(func() { // after txSender.Close() - result := txSender.SendTransaction(tests.Context(t), nil) - assert.EqualError(t, result.err, "TransactionSender not started") - }) - - _, txSender = newTestTransactionSender(t, chainID, logger.Test(t), - []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode}, - []SendOnlyNode[types.ID, TestSendTxRPCClient]{slowSendOnly}) - }) - t.Run("Returns error if there is no healthy primary nodes", func(t *testing.T) { - chainID := types.RandomID() - primary := newNodeWithState(t, nodeStateUnreachable, nil, nil) - sendOnly := newNodeWithState(t, nodeStateUnreachable, nil, nil) - - lggr := logger.Test(t) - - _, txSender := newTestTransactionSender(t, chainID, lggr, - []Node[types.ID, TestSendTxRPCClient]{primary}, - []SendOnlyNode[types.ID, TestSendTxRPCClient]{sendOnly}) - - result := txSender.SendTransaction(tests.Context(t), nil) - assert.EqualError(t, result.Error(), ErroringNodeError.Error()) - }) - - t.Run("Transaction success even if one of the nodes is unhealthy", func(t *testing.T) { - chainID := types.RandomID() - mainNode := newNode(t, nil, nil) - unexpectedCall := func(args mock.Arguments) { - panic("SendTx must not be called for unhealthy node") - } - unhealthyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) - unhealthySendOnlyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) - - lggr := logger.Test(t) - - _, txSender := newTestTransactionSender(t, chainID, lggr, - []Node[types.ID, TestSendTxRPCClient]{mainNode, unhealthyNode}, - []SendOnlyNode[types.ID, TestSendTxRPCClient]{unhealthySendOnlyNode}) - - result := txSender.SendTransaction(tests.Context(t), nil) - require.NoError(t, result.Error()) - require.Equal(t, Successful, result.Code()) - }) - t.Run("All background jobs stop even if RPC returns result after soft timeout", func(t *testing.T) { - chainID := types.RandomID() - expectedError := errors.New("transaction failed") - fastNode := newNode(t, expectedError, nil) - - // hold reply from the node till SendTransaction returns result - sendTxContext, sendTxCancel := context.WithCancel(tests.Context(t)) - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - <-sendTxContext.Done() - }) - - lggr := logger.Test(t) - - _, txSender := newTestTransactionSender(t, chainID, lggr, []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode}, nil) - result := txSender.SendTransaction(sendTxContext, nil) - sendTxCancel() - require.EqualError(t, result.Error(), expectedError.Error()) - // TxSender should stop all background go routines after SendTransaction is done and before test is done. - // Otherwise, it signals that we have a goroutine leak. - txSender.wg.Wait() - }) -} - -func TestTransactionSender_SendTransaction_aggregateTxResults(t *testing.T) { - t.Parallel() - // ensure failure on new SendTxReturnCode - codesToCover := map[SendTxReturnCode]struct{}{} - for code := Successful; code < sendTxReturnCodeLen; code++ { - codesToCover[code] = struct{}{} - } - - testCases := []struct { - Name string - ExpectedTxResult string - ExpectedCriticalErr string - ResultsByCode sendTxResults[*sendTxResult] - }{ - { - Name: "Returns success and logs critical error on success and Fatal", - ExpectedTxResult: "success", - ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", - ResultsByCode: sendTxResults[*sendTxResult]{ - Successful: {NewSendTxResult(errors.New("success"))}, - Fatal: {NewSendTxResult(errors.New("fatal"))}, - }, - }, - { - Name: "Returns TransactionAlreadyKnown and logs critical error on TransactionAlreadyKnown and Fatal", - ExpectedTxResult: "tx_already_known", - ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", - ResultsByCode: sendTxResults[*sendTxResult]{ - TransactionAlreadyKnown: {NewSendTxResult(errors.New("tx_already_known"))}, - Unsupported: {NewSendTxResult(errors.New("unsupported"))}, - }, - }, - { - Name: "Prefers sever error to temporary", - ExpectedTxResult: "underpriced", - ExpectedCriticalErr: "", - ResultsByCode: sendTxResults[*sendTxResult]{ - Retryable: {NewSendTxResult(errors.New("retryable"))}, - Underpriced: {NewSendTxResult(errors.New("underpriced"))}, - }, - }, - { - Name: "Returns temporary error", - ExpectedTxResult: "retryable", - ExpectedCriticalErr: "", - ResultsByCode: sendTxResults[*sendTxResult]{ - Retryable: {NewSendTxResult(errors.New("retryable"))}, - }, - }, - { - Name: "Insufficient funds is treated as error", - ExpectedTxResult: "insufficientFunds", - ExpectedCriticalErr: "", - ResultsByCode: sendTxResults[*sendTxResult]{ - InsufficientFunds: {NewSendTxResult(errors.New("insufficientFunds"))}, - }, - }, - { - Name: "Logs critical error on empty ResultsByCode", - ExpectedCriticalErr: "expected at least one response on SendTransaction", - ResultsByCode: sendTxResults[*sendTxResult]{}, - }, - { - Name: "Zk terminally stuck", - ExpectedTxResult: "not enough keccak counters to continue the execution", - ExpectedCriticalErr: "", - ResultsByCode: sendTxResults[*sendTxResult]{ - TerminallyStuck: {NewSendTxResult(errors.New("not enough keccak counters to continue the execution"))}, - }, - }, - } - - for _, testCase := range testCases { - for code := range testCase.ResultsByCode { - delete(codesToCover, code) - } - - t.Run(testCase.Name, func(t *testing.T) { - txResult, err := aggregateTxResults(testCase.ResultsByCode) - if testCase.ExpectedTxResult != "" { - require.EqualError(t, txResult.Error(), testCase.ExpectedTxResult) - } - - logger.Sugared(logger.Test(t)).Info("Map: " + fmt.Sprint(testCase.ResultsByCode)) - logger.Sugared(logger.Test(t)).Criticalw("observed invariant violation on SendTransaction", "resultsByCode", testCase.ResultsByCode, "err", err) - - if testCase.ExpectedCriticalErr == "" { - require.NoError(t, err) - } else { - require.EqualError(t, err, testCase.ExpectedCriticalErr) - } - }) - } - - // explicitly signal that following codes are properly handled in aggregateTxResults, - // but dedicated test cases won't be beneficial - for _, codeToIgnore := range []SendTxReturnCode{Unknown, ExceedsMaxFee, FeeOutOfValidRange} { - delete(codesToCover, codeToIgnore) - } - assert.Empty(t, codesToCover, "all of the SendTxReturnCode must be covered by this test") -} diff --git a/common/client/types.go b/common/client/types.go deleted file mode 100644 index 38880397442..00000000000 --- a/common/client/types.go +++ /dev/null @@ -1,83 +0,0 @@ -package client - -import ( - "context" - "math/big" - - "github.com/smartcontractkit/chainlink/v2/common/types" -) - -// RPCClient includes all the necessary generalized RPC methods used by Node to perform health checks -type RPCClient[ - CHAIN_ID types.ID, - HEAD Head, -] interface { - // ChainID - fetches ChainID from the RPC to verify that it matches config - ChainID(ctx context.Context) (CHAIN_ID, error) - // Dial - prepares the RPC for usage. Can be called on fresh or closed RPC - Dial(ctx context.Context) error - // SubscribeToHeads - returns channel and subscription for new heads. - SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) - // SubscribeToFinalizedHeads - returns channel and subscription for finalized heads. - SubscribeToFinalizedHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) - // Ping - returns error if RPC is not reachable - Ping(context.Context) error - // IsSyncing - returns true if the RPC is in Syncing state and can not process calls - IsSyncing(ctx context.Context) (bool, error) - // UnsubscribeAllExcept - close all subscriptions except `subs` - UnsubscribeAllExcept(subs ...types.Subscription) - // Close - closes all subscriptions and aborts all RPC calls - Close() - // GetInterceptedChainInfo - returns latest and highest observed by application layer ChainInfo. - // latest ChainInfo is the most recent value received within a NodeClient's current lifecycle between Dial and DisconnectAll. - // highestUserObservations ChainInfo is the highest ChainInfo observed excluding health checks calls. - // Its values must not be reset. - // The results of corresponding calls, to get the most recent head and the latest finalized head, must be - // intercepted and reflected in ChainInfo before being returned to a caller. Otherwise, MultiNode is not able to - // provide repeatable read guarantee. - // DisconnectAll must reset latest ChainInfo to default value. - // Ensure implementation does not have a race condition when values are reset before request completion and as - // a result latest ChainInfo contains information from the previous cycle. - GetInterceptedChainInfo() (latest, highestUserObservations ChainInfo) -} - -// Head is the interface required by the NodeClient -type Head interface { - BlockNumber() int64 - BlockDifficulty() *big.Int - IsValid() bool -} - -// PoolChainInfoProvider - provides aggregation of nodes pool ChainInfo -type PoolChainInfoProvider interface { - // LatestChainInfo - returns number of live nodes available in the pool, 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. - // Returns highest latest ChainInfo within the alive nodes. E.g. most recent block number and highest block number - // observed by Node A are 10 and 15; Node B - 12 and 14. This method will return 12. - LatestChainInfo() (int, ChainInfo) - // HighestUserObservations - returns highest ChainInfo ever observed by any user of MultiNode. - HighestUserObservations() ChainInfo -} - -// ChainInfo - defines RPC's or MultiNode's view on the chain -type ChainInfo struct { - BlockNumber int64 - FinalizedBlockNumber int64 - TotalDifficulty *big.Int -} - -func MaxTotalDifficulty(a, b *big.Int) *big.Int { - if a == nil { - if b == nil { - return nil - } - - return big.NewInt(0).Set(b) - } - - if b == nil || a.Cmp(b) >= 0 { - return big.NewInt(0).Set(a) - } - - return big.NewInt(0).Set(b) -} diff --git a/common/client/types_test.go b/common/client/types_test.go deleted file mode 100644 index 68d7a3fe78e..00000000000 --- a/common/client/types_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package client - -import ( - "math/big" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMaxDifficulty(t *testing.T) { - cases := []struct { - A, B, Result *big.Int - }{ - { - A: nil, B: nil, Result: nil, - }, - { - A: nil, B: big.NewInt(1), Result: big.NewInt(1), - }, - { - A: big.NewInt(1), B: big.NewInt(1), Result: big.NewInt(1), - }, - { - A: big.NewInt(1), B: big.NewInt(2), Result: big.NewInt(2), - }, - } - - for _, test := range cases { - actualResult := MaxTotalDifficulty(test.A, test.B) - assert.Equal(t, test.Result, actualResult, "expected max(%v, %v) to produce %v", test.A, test.B, test.Result) - inverted := MaxTotalDifficulty(test.B, test.A) - assert.Equal(t, actualResult, inverted, "expected max(%v, %v) == max(%v, %v)", test.A, test.B, test.B, test.A) - } -} diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 14e959c39ae..41ec0f644b4 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -18,8 +18,8 @@ import ( "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-framework/multinode" - "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" @@ -498,7 +498,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand errType, err = eb.validateOnChainSequence(ctx, lgr, errType, err, etx, retryCount) } - if errType == client.Fatal || errType == client.TerminallyStuck { + if errType == multinode.Fatal || errType == multinode.TerminallyStuck { eb.SvcErrBuffer.Append(err) etx.Error = null.StringFrom(err.Error()) return eb.saveFatallyErroredTransaction(lgr, &etx), true @@ -508,9 +508,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand etx.BroadcastAt = &initialBroadcastAt switch errType { - case client.TransactionAlreadyKnown: + case multinode.TransactionAlreadyKnown: fallthrough - case client.Successful: + case multinode.Successful: // Either the transaction was successful or one of the following four scenarios happened: // // SCENARIO 1 @@ -557,14 +557,14 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // Increment sequence if successfully broadcasted eb.sequenceTracker.GenerateNextSequence(etx.FromAddress, *etx.Sequence) return err, true - case client.Underpriced: + case multinode.Underpriced: bumpedAttempt, retryable, replaceErr := eb.replaceAttemptWithBumpedGas(ctx, lgr, err, etx, attempt) if replaceErr != nil { return replaceErr, retryable } return eb.handleInProgressTx(ctx, etx, bumpedAttempt, initialBroadcastAt, retryCount+1) - case client.InsufficientFunds: + case multinode.InsufficientFunds: // NOTE: This can occur due to either insufficient funds or a gas spike // combined with a high gas limit. Regardless of the cause, we need to obtain a new estimate, // replace the current attempt, and retry after the backoff duration. @@ -574,9 +574,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand return replaceErr, true } return err, true - case client.Retryable: + case multinode.Retryable: return err, true - case client.FeeOutOfValidRange: + case multinode.FeeOutOfValidRange: replacementAttempt, retryable, replaceErr := eb.replaceAttemptWithNewEstimation(ctx, lgr, etx, attempt) if replaceErr != nil { return replaceErr, retryable @@ -585,9 +585,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand lgr.Warnw("L2 rejected transaction due to incorrect fee, re-estimated and will try again", "etxID", etx.ID, "err", err, "newGasPrice", replacementAttempt.TxFee, "newGasLimit", replacementAttempt.ChainSpecificFeeLimit) return eb.handleInProgressTx(ctx, etx, *replacementAttempt, initialBroadcastAt, 0) - case client.Unsupported: + case multinode.Unsupported: return err, false - case client.ExceedsMaxFee: + case multinode.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 @@ -600,7 +600,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 client.Unknown: + case multinode.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`, "attempt", attempt, "err", err) @@ -632,9 +632,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand } } -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) validateOnChainSequence(ctx context.Context, lgr logger.SugaredLogger, errType client.SendTxReturnCode, err error, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], retryCount int) (client.SendTxReturnCode, error) { +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) validateOnChainSequence(ctx context.Context, lgr logger.SugaredLogger, errType multinode.SendTxReturnCode, err error, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], retryCount int) (multinode.SendTxReturnCode, error) { // Only check if sequence was incremented if broadcast was successful, otherwise return the existing err type - if errType != client.Successful { + if errType != multinode.Successful { return errType, err } // Transaction sequence cannot be nil here since a sequence is required to broadcast @@ -649,7 +649,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) vali // Insufficient transaction fee is a common scenario in which the sequence is not incremented by the chain even though we got a successful response // If the sequence failed to increment and hasn't reached the max retries, return the Underpriced error to try again with a bumped attempt if nextSeqOnChain.Int64() == txSeq.Int64() && retryCount < maxHederaBroadcastRetries { - return client.Underpriced, nil + return multinode.Underpriced, nil } // If the transaction reaches the retry limit and fails to get included, mark it as fatally errored @@ -657,17 +657,17 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) vali if nextSeqOnChain.Int64() == txSeq.Int64() && retryCount >= maxHederaBroadcastRetries { err := fmt.Errorf("failed to broadcast transaction on %s after %d retries", hederaChainType, retryCount) lgr.Error(err.Error()) - return client.Fatal, err + return multinode.Fatal, err } // Belts and braces approach to detect and handle sqeuence gaps if the broadcast is considered successful if nextSeqOnChain.Int64() < txSeq.Int64() { err := fmt.Errorf("next expected sequence on-chain (%s) is less than the broadcasted transaction's sequence (%s)", nextSeqOnChain.String(), txSeq.String()) lgr.Criticalw("Sequence gap has been detected and needs to be filled", "error", err) - return client.Fatal, err + return multinode.Fatal, err } - return client.Successful, nil + return multinode.Successful, nil } // Finds next transaction in the queue, assigns a sequence, and moves it to "in_progress" state ready for broadcast. diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 7c5ba798cf2..7b5a90e70a6 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -20,8 +20,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-framework/multinode" - "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" iutils "github.com/smartcontractkit/chainlink/v2/common/internal/utils" @@ -696,7 +696,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 client.Underpriced: + case multinode.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", "attempt", attempt) @@ -731,7 +731,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han return fmt.Errorf("saveReplacementInProgressAttempt failed: %w", err) } return ec.handleInProgressAttempt(ctx, lggr, etx, replacementAttempt, blockHeight) - case client.ExceedsMaxFee: + case multinode.ExceedsMaxFee: // Confirmer: Note it is not guaranteed that all nodes share the same tx fee cap. // So it is very likely that this attempt was successful on another node since // it was already successfully broadcasted. So we assume it is successful and @@ -745,7 +745,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han lggr.Criticalw(`RPC node rejected this tx as outside Fee Cap but it may have been accepted by another Node`, "attempt", attempt) timeout := ec.dbConfig.DefaultQueryTimeout() return ec.txStore.SaveSentAttempt(ctx, timeout, &attempt, now) - case client.Fatal: + case multinode.Fatal: // WARNING: This should never happen! // Should NEVER be fatal this is an invariant violation. The // Broadcaster can never create a TxAttempt that will @@ -760,7 +760,7 @@ 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 client.TerminallyStuck: + case multinode.TerminallyStuck: // A transaction could broadcast successfully but then be considered terminally stuck on another attempt // Even though the transaction can succeed under different circumstances, we want to purge this transaction as soon as we get this error lggr.Warnw("terminally stuck transaction detected", "err", sendError.Error()) @@ -775,20 +775,20 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han return fmt.Errorf("saveReplacementInProgressAttempt failed: %w", err) } return ec.handleInProgressAttempt(ctx, lggr, etx, purgeAttempt, blockHeight) - case client.TransactionAlreadyKnown: + case multinode.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()) timeout := ec.dbConfig.DefaultQueryTimeout() return ec.txStore.SaveConfirmedAttempt(ctx, timeout, &attempt, now) - case client.InsufficientFunds: + case multinode.InsufficientFunds: timeout := ec.dbConfig.DefaultQueryTimeout() return ec.txStore.SaveInsufficientFundsAttempt(ctx, timeout, &attempt, now) - case client.Successful: + case multinode.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 client.Unknown: + case multinode.Unknown: // Every error that doesn't fall under one of the above categories will be treated as Unknown. fallthrough default: @@ -838,7 +838,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", attempt.ChainSpecificFeeLimit, "callerProvidedFeeLimit", etx.FeeLimit, "attempt", attempt) - if errCode, err := ec.client.SendTransactionReturnCode(ctx, *etx, attempt, ec.lggr); errCode != client.Successful && err != nil { + if errCode, err := ec.client.SendTransactionReturnCode(ctx, *etx, attempt, ec.lggr); errCode != multinode.Successful && err != nil { ec.lggr.Errorw(fmt.Sprintf("ForceRebroadcast: failed to rebroadcast tx %v with sequence %v, gas limit %v, and caller provided fee Limit %v : %s", etx.ID, *etx.Sequence, attempt.ChainSpecificFeeLimit, etx.FeeLimit, err.Error()), "err", err, "fee", attempt.TxFee) continue } diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index d4f1b4275a1..6b65ca05923 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -9,7 +9,7 @@ import ( "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" + "github.com/smartcontractkit/chainlink-framework/multinode" 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" @@ -184,13 +184,13 @@ func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) resendUnco return nil } -func logResendResult(lggr logger.Logger, codes []client.SendTxReturnCode) { +func logResendResult(lggr logger.Logger, codes []multinode.SendTxReturnCode) { var nNew int var nFatal int for _, c := range codes { - if c == client.Successful { + if c == multinode.Successful { nNew++ - } else if c == client.Fatal { + } else if c == multinode.Fatal { nFatal++ } } diff --git a/common/txmgr/types/client.go b/common/txmgr/types/client.go index 759b15d6162..67426437c8a 100644 --- a/common/txmgr/types/client.go +++ b/common/txmgr/types/client.go @@ -7,7 +7,8 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink-framework/multinode" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" ) @@ -49,7 +50,7 @@ type TransactionClient[ bathSize int, lggr logger.SugaredLogger, ) ( - txCodes []client.SendTxReturnCode, + txCodes []multinode.SendTxReturnCode, txErrs []error, broadcastTime time.Time, successfulTxIDs []int64, @@ -59,7 +60,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.SugaredLogger, - ) (client.SendTxReturnCode, error) + ) (multinode.SendTxReturnCode, error) SendEmptyTransaction( ctx context.Context, newTxAttempt func(ctx context.Context, seq SEQ, feeLimit uint64, fee FEE, fromAddress ADDR) (attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error), diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go deleted file mode 100644 index b00c155ccc7..00000000000 --- a/common/types/mocks/head.go +++ /dev/null @@ -1,607 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package mocks - -import ( - big "math/big" - time "time" - - mock "github.com/stretchr/testify/mock" - - types "github.com/smartcontractkit/chainlink/v2/common/types" -) - -// Head is an autogenerated mock type for the Head type -type Head[BLOCK_HASH types.Hashable] struct { - mock.Mock -} - -type Head_Expecter[BLOCK_HASH types.Hashable] struct { - mock *mock.Mock -} - -func (_m *Head[BLOCK_HASH]) EXPECT() *Head_Expecter[BLOCK_HASH] { - return &Head_Expecter[BLOCK_HASH]{mock: &_m.Mock} -} - -// BlockDifficulty provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) BlockDifficulty() *big.Int { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for BlockDifficulty") - } - - 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).(*big.Int) - } - } - - return r0 -} - -// Head_BlockDifficulty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockDifficulty' -type Head_BlockDifficulty_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// BlockDifficulty is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) BlockDifficulty() *Head_BlockDifficulty_Call[BLOCK_HASH] { - return &Head_BlockDifficulty_Call[BLOCK_HASH]{Call: _e.mock.On("BlockDifficulty")} -} - -func (_c *Head_BlockDifficulty_Call[BLOCK_HASH]) Run(run func()) *Head_BlockDifficulty_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_BlockDifficulty_Call[BLOCK_HASH]) Return(_a0 *big.Int) *Head_BlockDifficulty_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_BlockDifficulty_Call[BLOCK_HASH]) RunAndReturn(run func() *big.Int) *Head_BlockDifficulty_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// BlockHash provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) BlockHash() BLOCK_HASH { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for BlockHash") - } - - var r0 BLOCK_HASH - if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(BLOCK_HASH) - } - } - - return r0 -} - -// Head_BlockHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockHash' -type Head_BlockHash_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// BlockHash is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) BlockHash() *Head_BlockHash_Call[BLOCK_HASH] { - return &Head_BlockHash_Call[BLOCK_HASH]{Call: _e.mock.On("BlockHash")} -} - -func (_c *Head_BlockHash_Call[BLOCK_HASH]) Run(run func()) *Head_BlockHash_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_BlockHash_Call[BLOCK_HASH]) Return(_a0 BLOCK_HASH) *Head_BlockHash_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_BlockHash_Call[BLOCK_HASH]) RunAndReturn(run func() BLOCK_HASH) *Head_BlockHash_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// BlockNumber provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) BlockNumber() int64 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for BlockNumber") - } - - var r0 int64 - if rf, ok := ret.Get(0).(func() int64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int64) - } - - return r0 -} - -// Head_BlockNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockNumber' -type Head_BlockNumber_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// BlockNumber is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) BlockNumber() *Head_BlockNumber_Call[BLOCK_HASH] { - return &Head_BlockNumber_Call[BLOCK_HASH]{Call: _e.mock.On("BlockNumber")} -} - -func (_c *Head_BlockNumber_Call[BLOCK_HASH]) Run(run func()) *Head_BlockNumber_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_BlockNumber_Call[BLOCK_HASH]) Return(_a0 int64) *Head_BlockNumber_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_BlockNumber_Call[BLOCK_HASH]) RunAndReturn(run func() int64) *Head_BlockNumber_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// ChainLength provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) ChainLength() uint32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ChainLength") - } - - var r0 uint32 - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - return r0 -} - -// Head_ChainLength_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainLength' -type Head_ChainLength_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// ChainLength is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) ChainLength() *Head_ChainLength_Call[BLOCK_HASH] { - return &Head_ChainLength_Call[BLOCK_HASH]{Call: _e.mock.On("ChainLength")} -} - -func (_c *Head_ChainLength_Call[BLOCK_HASH]) Run(run func()) *Head_ChainLength_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_ChainLength_Call[BLOCK_HASH]) Return(_a0 uint32) *Head_ChainLength_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_ChainLength_Call[BLOCK_HASH]) RunAndReturn(run func() uint32) *Head_ChainLength_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// EarliestHeadInChain provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) EarliestHeadInChain() types.Head[BLOCK_HASH] { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for EarliestHeadInChain") - } - - var r0 types.Head[BLOCK_HASH] - if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.Head[BLOCK_HASH]) - } - } - - return r0 -} - -// Head_EarliestHeadInChain_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EarliestHeadInChain' -type Head_EarliestHeadInChain_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// EarliestHeadInChain is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) EarliestHeadInChain() *Head_EarliestHeadInChain_Call[BLOCK_HASH] { - return &Head_EarliestHeadInChain_Call[BLOCK_HASH]{Call: _e.mock.On("EarliestHeadInChain")} -} - -func (_c *Head_EarliestHeadInChain_Call[BLOCK_HASH]) Run(run func()) *Head_EarliestHeadInChain_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_EarliestHeadInChain_Call[BLOCK_HASH]) Return(_a0 types.Head[BLOCK_HASH]) *Head_EarliestHeadInChain_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_EarliestHeadInChain_Call[BLOCK_HASH]) RunAndReturn(run func() types.Head[BLOCK_HASH]) *Head_EarliestHeadInChain_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// GetParent provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) GetParent() types.Head[BLOCK_HASH] { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetParent") - } - - var r0 types.Head[BLOCK_HASH] - if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.Head[BLOCK_HASH]) - } - } - - return r0 -} - -// Head_GetParent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetParent' -type Head_GetParent_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// GetParent is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) GetParent() *Head_GetParent_Call[BLOCK_HASH] { - return &Head_GetParent_Call[BLOCK_HASH]{Call: _e.mock.On("GetParent")} -} - -func (_c *Head_GetParent_Call[BLOCK_HASH]) Run(run func()) *Head_GetParent_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_GetParent_Call[BLOCK_HASH]) Return(_a0 types.Head[BLOCK_HASH]) *Head_GetParent_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_GetParent_Call[BLOCK_HASH]) RunAndReturn(run func() types.Head[BLOCK_HASH]) *Head_GetParent_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// GetParentHash provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) GetParentHash() BLOCK_HASH { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetParentHash") - } - - var r0 BLOCK_HASH - if rf, ok := ret.Get(0).(func() BLOCK_HASH); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(BLOCK_HASH) - } - } - - return r0 -} - -// Head_GetParentHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetParentHash' -type Head_GetParentHash_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// GetParentHash is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) GetParentHash() *Head_GetParentHash_Call[BLOCK_HASH] { - return &Head_GetParentHash_Call[BLOCK_HASH]{Call: _e.mock.On("GetParentHash")} -} - -func (_c *Head_GetParentHash_Call[BLOCK_HASH]) Run(run func()) *Head_GetParentHash_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_GetParentHash_Call[BLOCK_HASH]) Return(_a0 BLOCK_HASH) *Head_GetParentHash_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_GetParentHash_Call[BLOCK_HASH]) RunAndReturn(run func() BLOCK_HASH) *Head_GetParentHash_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// GetTimestamp provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) GetTimestamp() time.Time { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetTimestamp") - } - - 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 -} - -// Head_GetTimestamp_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTimestamp' -type Head_GetTimestamp_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// GetTimestamp is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) GetTimestamp() *Head_GetTimestamp_Call[BLOCK_HASH] { - return &Head_GetTimestamp_Call[BLOCK_HASH]{Call: _e.mock.On("GetTimestamp")} -} - -func (_c *Head_GetTimestamp_Call[BLOCK_HASH]) Run(run func()) *Head_GetTimestamp_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_GetTimestamp_Call[BLOCK_HASH]) Return(_a0 time.Time) *Head_GetTimestamp_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_GetTimestamp_Call[BLOCK_HASH]) RunAndReturn(run func() time.Time) *Head_GetTimestamp_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// HashAtHeight provides a mock function with given fields: blockNum -func (_m *Head[BLOCK_HASH]) HashAtHeight(blockNum int64) BLOCK_HASH { - ret := _m.Called(blockNum) - - if len(ret) == 0 { - panic("no return value specified for HashAtHeight") - } - - var r0 BLOCK_HASH - if rf, ok := ret.Get(0).(func(int64) BLOCK_HASH); ok { - r0 = rf(blockNum) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(BLOCK_HASH) - } - } - - return r0 -} - -// Head_HashAtHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HashAtHeight' -type Head_HashAtHeight_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// HashAtHeight is a helper method to define mock.On call -// - blockNum int64 -func (_e *Head_Expecter[BLOCK_HASH]) HashAtHeight(blockNum interface{}) *Head_HashAtHeight_Call[BLOCK_HASH] { - return &Head_HashAtHeight_Call[BLOCK_HASH]{Call: _e.mock.On("HashAtHeight", blockNum)} -} - -func (_c *Head_HashAtHeight_Call[BLOCK_HASH]) Run(run func(blockNum int64)) *Head_HashAtHeight_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int64)) - }) - return _c -} - -func (_c *Head_HashAtHeight_Call[BLOCK_HASH]) Return(_a0 BLOCK_HASH) *Head_HashAtHeight_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_HashAtHeight_Call[BLOCK_HASH]) RunAndReturn(run func(int64) BLOCK_HASH) *Head_HashAtHeight_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// HeadAtHeight provides a mock function with given fields: blockNum -func (_m *Head[BLOCK_HASH]) HeadAtHeight(blockNum int64) (types.Head[BLOCK_HASH], error) { - ret := _m.Called(blockNum) - - if len(ret) == 0 { - panic("no return value specified for HeadAtHeight") - } - - var r0 types.Head[BLOCK_HASH] - var r1 error - if rf, ok := ret.Get(0).(func(int64) (types.Head[BLOCK_HASH], error)); ok { - return rf(blockNum) - } - if rf, ok := ret.Get(0).(func(int64) types.Head[BLOCK_HASH]); ok { - r0 = rf(blockNum) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.Head[BLOCK_HASH]) - } - } - - if rf, ok := ret.Get(1).(func(int64) error); ok { - r1 = rf(blockNum) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Head_HeadAtHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeadAtHeight' -type Head_HeadAtHeight_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// HeadAtHeight is a helper method to define mock.On call -// - blockNum int64 -func (_e *Head_Expecter[BLOCK_HASH]) HeadAtHeight(blockNum interface{}) *Head_HeadAtHeight_Call[BLOCK_HASH] { - return &Head_HeadAtHeight_Call[BLOCK_HASH]{Call: _e.mock.On("HeadAtHeight", blockNum)} -} - -func (_c *Head_HeadAtHeight_Call[BLOCK_HASH]) Run(run func(blockNum int64)) *Head_HeadAtHeight_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int64)) - }) - return _c -} - -func (_c *Head_HeadAtHeight_Call[BLOCK_HASH]) Return(_a0 types.Head[BLOCK_HASH], _a1 error) *Head_HeadAtHeight_Call[BLOCK_HASH] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *Head_HeadAtHeight_Call[BLOCK_HASH]) RunAndReturn(run func(int64) (types.Head[BLOCK_HASH], error)) *Head_HeadAtHeight_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// IsValid provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) IsValid() bool { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for IsValid") - } - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// Head_IsValid_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsValid' -type Head_IsValid_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// IsValid is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) IsValid() *Head_IsValid_Call[BLOCK_HASH] { - return &Head_IsValid_Call[BLOCK_HASH]{Call: _e.mock.On("IsValid")} -} - -func (_c *Head_IsValid_Call[BLOCK_HASH]) Run(run func()) *Head_IsValid_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_IsValid_Call[BLOCK_HASH]) Return(_a0 bool) *Head_IsValid_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_IsValid_Call[BLOCK_HASH]) RunAndReturn(run func() bool) *Head_IsValid_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// LatestFinalizedHead provides a mock function with no fields -func (_m *Head[BLOCK_HASH]) LatestFinalizedHead() types.Head[BLOCK_HASH] { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for LatestFinalizedHead") - } - - var r0 types.Head[BLOCK_HASH] - if rf, ok := ret.Get(0).(func() types.Head[BLOCK_HASH]); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.Head[BLOCK_HASH]) - } - } - - return r0 -} - -// Head_LatestFinalizedHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestFinalizedHead' -type Head_LatestFinalizedHead_Call[BLOCK_HASH types.Hashable] struct { - *mock.Call -} - -// LatestFinalizedHead is a helper method to define mock.On call -func (_e *Head_Expecter[BLOCK_HASH]) LatestFinalizedHead() *Head_LatestFinalizedHead_Call[BLOCK_HASH] { - return &Head_LatestFinalizedHead_Call[BLOCK_HASH]{Call: _e.mock.On("LatestFinalizedHead")} -} - -func (_c *Head_LatestFinalizedHead_Call[BLOCK_HASH]) Run(run func()) *Head_LatestFinalizedHead_Call[BLOCK_HASH] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Head_LatestFinalizedHead_Call[BLOCK_HASH]) Return(_a0 types.Head[BLOCK_HASH]) *Head_LatestFinalizedHead_Call[BLOCK_HASH] { - _c.Call.Return(_a0) - return _c -} - -func (_c *Head_LatestFinalizedHead_Call[BLOCK_HASH]) RunAndReturn(run func() types.Head[BLOCK_HASH]) *Head_LatestFinalizedHead_Call[BLOCK_HASH] { - _c.Call.Return(run) - return _c -} - -// NewHead creates a new instance of Head. 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 NewHead[BLOCK_HASH types.Hashable](t interface { - mock.TestingT - Cleanup(func()) -}) *Head[BLOCK_HASH] { - mock := &Head[BLOCK_HASH]{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 0835b4c0ed8..612a95b7461 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -14,7 +14,7 @@ import ( 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-framework/multinode" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -60,7 +60,7 @@ type Client interface { // to use HeadTracker to get latest finalized block. LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) - SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) + SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (multinode.SendTxReturnCode, error) // Wrapped Geth client methods // blockNumber can be specified as `nil` to imply latest block @@ -97,11 +97,11 @@ type Client interface { } type chainClient struct { - multiNode *commonclient.MultiNode[ + multiNode *multinode.MultiNode[ *big.Int, *RPCClient, ] - txSender *commonclient.TransactionSender[*types.Transaction, *SendTxResult, *big.Int, *RPCClient] + txSender *multinode.TransactionSender[*types.Transaction, *SendTxResult, *big.Int, *RPCClient] logger logger.SugaredLogger chainType chaintype.ChainType clientErrors evmconfig.ClientErrors @@ -111,15 +111,15 @@ func NewChainClient( lggr logger.Logger, selectionMode string, leaseDuration time.Duration, - nodes []commonclient.Node[*big.Int, *RPCClient], - sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient], + nodes []multinode.Node[*big.Int, *RPCClient], + sendonlys []multinode.SendOnlyNode[*big.Int, *RPCClient], chainID *big.Int, clientErrors evmconfig.ClientErrors, deathDeclarationDelay time.Duration, chainType chaintype.ChainType, ) Client { chainFamily := "EVM" - multiNode := commonclient.NewMultiNode[*big.Int, *RPCClient]( + multiNode := multinode.NewMultiNode[*big.Int, *RPCClient]( lggr, selectionMode, leaseDuration, @@ -130,7 +130,7 @@ func NewChainClient( deathDeclarationDelay, ) - txSender := commonclient.NewTransactionSender[*types.Transaction, *SendTxResult, *big.Int, *RPCClient]( + txSender := multinode.NewTransactionSender[*types.Transaction, *SendTxResult, *big.Int, *RPCClient]( lggr, chainID, chainFamily, @@ -389,7 +389,7 @@ func (c *chainClient) SendTransaction(ctx context.Context, tx *types.Transaction return result.Error() } -func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { +func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (multinode.SendTxReturnCode, error) { err := c.SendTransaction(ctx, tx) returnCode := ClassifySendError(err, c.clientErrors, c.logger, tx, fromAddress, c.IsL2()) return returnCode, err diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go index 77e11db7a90..e775feb1434 100644 --- a/core/chains/evm/client/chain_client_test.go +++ b/core/chains/evm/client/chain_client_test.go @@ -25,8 +25,8 @@ import ( "github.com/tidwall/gjson" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-framework/multinode" - 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/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -39,7 +39,7 @@ func mustNewChainClient(t *testing.T, wsURL string, sendonlys ...url.URL) client func mustNewChainClientWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) client.Client { cfg := client.TestNodePoolConfig{ - NodeSelectionMode: commonclient.NodeSelectionModeRoundRobin, + NodeSelectionMode: multinode.NodeSelectionModeRoundRobin, } c, err := client.NewChainClientWithTestNode(t, cfg, time.Second*0, cfg.NodeLeaseDuration, wsURL, nil, sendonlys, 42, chainID) require.NoError(t, err) @@ -491,7 +491,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commonclient.Fatal) + assert.Equal(t, multinode.Fatal, errType) }) t.Run("returns TransactionAlreadyKnown error type when error message is nonce too low", func(t *testing.T) { @@ -517,7 +517,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commonclient.TransactionAlreadyKnown) + assert.Equal(t, multinode.TransactionAlreadyKnown, errType) }) t.Run("returns Successful error type when there is no error message", func(t *testing.T) { @@ -542,7 +542,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.NoError(t, err) - assert.Equal(t, errType, commonclient.Successful) + assert.Equal(t, multinode.Successful, errType) }) t.Run("returns Underpriced error type when transaction is terminally underpriced", func(t *testing.T) { @@ -568,7 +568,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commonclient.Underpriced) + assert.Equal(t, multinode.Underpriced, errType) }) t.Run("returns Unsupported error type when error message is queue full", func(t *testing.T) { @@ -594,7 +594,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commonclient.Unsupported) + assert.Equal(t, multinode.Unsupported, errType) }) t.Run("returns Retryable error type when there is a transaction gap", func(t *testing.T) { @@ -620,7 +620,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commonclient.Retryable) + assert.Equal(t, multinode.Retryable, errType) }) t.Run("returns InsufficientFunds error type when the sender address doesn't have enough funds", func(t *testing.T) { @@ -646,7 +646,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commonclient.InsufficientFunds) + assert.Equal(t, multinode.InsufficientFunds, errType) }) t.Run("returns ExceedsFeeCap error type when gas price is too high for the node", func(t *testing.T) { @@ -672,7 +672,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commonclient.ExceedsMaxFee) + assert.Equal(t, multinode.ExceedsMaxFee, errType) }) t.Run("returns Unknown error type when the error can't be categorized", func(t *testing.T) { @@ -698,7 +698,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(tests.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commonclient.Unknown) + assert.Equal(t, multinode.Unknown, errType) }) } @@ -793,34 +793,34 @@ func TestEthClient_ErroringClient(t *testing.T) { ctx := tests.Context(t) // Empty node means there are no active nodes to select from, causing client to always return error. - erroringClient := client.NewChainClientWithEmptyNode(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID) + erroringClient := client.NewChainClientWithEmptyNode(t, multinode.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID) _, err := erroringClient.BalanceAt(ctx, common.Address{}, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) err = erroringClient.BatchCallContext(ctx, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) err = erroringClient.BatchCallContextAll(ctx, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.BlockByHash(ctx, common.Hash{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.BlockByNumber(ctx, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) err = erroringClient.CallContext(ctx, nil, "") - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.CallContract(ctx, ethereum.CallMsg{}, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) id := erroringClient.ConfiguredChainID() require.Equal(t, id, big.NewInt(0)) _, err = erroringClient.CodeAt(ctx, common.Address{}, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) id = erroringClient.ConfiguredChainID() require.Equal(t, id, testutils.FixtureChainID) @@ -829,67 +829,67 @@ func TestEthClient_ErroringClient(t *testing.T) { require.ErrorContains(t, err, "no available nodes for chain") _, err = erroringClient.EstimateGas(ctx, ethereum.CallMsg{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.FilterLogs(ctx, ethereum.FilterQuery{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.HeaderByHash(ctx, common.Hash{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.HeaderByNumber(ctx, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.HeadByHash(ctx, common.Hash{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.HeadByNumber(ctx, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.LINKBalance(ctx, common.Address{}, common.Address{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.LatestBlockHeight(ctx) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.PendingCodeAt(ctx, common.Address{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.PendingNonceAt(ctx, common.Address{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) txSenderNotStarted := errors.New("TransactionSender not started") err = erroringClient.SendTransaction(ctx, nil) - require.Equal(t, err, txSenderNotStarted) + require.Equal(t, txSenderNotStarted, err) tx := testutils.NewLegacyTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) code, err := erroringClient.SendTransactionReturnCode(ctx, tx, common.Address{}) - require.Equal(t, code, commonclient.Unknown) - require.Equal(t, err, txSenderNotStarted) + require.Equal(t, multinode.Unknown, code) + require.Equal(t, txSenderNotStarted, err) _, err = erroringClient.NonceAt(ctx, common.Address{}, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, _, err = erroringClient.SubscribeToHeads(ctx) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.SuggestGasPrice(ctx) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.SuggestGasTipCap(ctx) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.TokenBalance(ctx, common.Address{}, common.Address{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.TransactionByHash(ctx, common.Hash{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) _, err = erroringClient.TransactionReceipt(ctx, common.Hash{}) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, multinode.ErrNodeError, err) } const headResult = client.HeadResult diff --git a/core/chains/evm/client/config_builder.go b/core/chains/evm/client/config_builder.go index 66bdfc2614f..7b412815557 100644 --- a/core/chains/evm/client/config_builder.go +++ b/core/chains/evm/client/config_builder.go @@ -8,8 +8,8 @@ import ( "go.uber.org/multierr" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-framework/multinode" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -44,7 +44,7 @@ func NewClientConfigs( noNewFinalizedHeadsThreshold time.Duration, finalizedBlockPollInterval time.Duration, newHeadsPollInterval time.Duration, -) (commonclient.ChainConfig, evmconfig.NodePool, []*toml.Node, error) { +) (multinode.ChainConfig, evmconfig.NodePool, []*toml.Node, error) { nodes, err := parseNodeConfigs(nodeCfgs) if err != nil { return nil, nil, nil, err diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index eaa33f041ac..871e574aea2 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -14,8 +14,8 @@ import ( pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-framework/multinode" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commontypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" ) @@ -407,7 +407,7 @@ func (s *SendError) IsServiceUnavailable(configErrors *ClientErrors) bool { return false } - return s.is(ServiceUnavailable, configErrors) || pkgerrors.Is(s.err, commonclient.ErroringNodeError) + return s.is(ServiceUnavailable, configErrors) || pkgerrors.Is(s.err, multinode.ErrNodeError) } // IsServiceTimeout indicates if the error was caused by a service timeout @@ -571,10 +571,10 @@ func ExtractRPCError(baseErr error) (*JsonError, error) { return &jErr, nil } -func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger.SugaredLogger, tx *types.Transaction, fromAddress common.Address, isL2 bool) commonclient.SendTxReturnCode { +func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger.SugaredLogger, tx *types.Transaction, fromAddress common.Address, isL2 bool) multinode.SendTxReturnCode { sendError := NewSendError(err) if sendError == nil { - return commonclient.Successful + return multinode.Successful } configErrors := ClientErrorRegexes(clientErrors) @@ -582,13 +582,13 @@ func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger. if sendError.Fatal(configErrors) { 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 commonclient.Fatal + return multinode.Fatal } if sendError.IsNonceTooLowError(configErrors) || sendError.IsTransactionAlreadyMined(configErrors) { lggr.Debugw(fmt.Sprintf("Transaction already confirmed for this nonce: %d", tx.Nonce()), "err", sendError, "etx", tx) // Nonce too low indicated that a transaction at this nonce was confirmed already. // Mark it as TransactionAlreadyKnown. - return commonclient.TransactionAlreadyKnown + return multinode.TransactionAlreadyKnown } if sendError.IsReplacementUnderpriced(configErrors) { lggr.Errorw(fmt.Sprintf("Replacement transaction underpriced for eth_tx %x. "+ @@ -596,57 +596,57 @@ func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger. tx.Hash()), "gasPrice", tx.GasPrice, "gasTipCap", tx.GasTipCap, "gasFeeCap", tx.GasFeeCap, "err", sendError, "etx", tx) // Assume success and hand off to the next cycle. - return commonclient.Successful + return multinode.Successful } if sendError.IsTransactionAlreadyInMempool(configErrors) { lggr.Debugw("Transaction already in mempool", "etx", tx, "err", sendError) - return commonclient.Successful + return multinode.Successful } if sendError.IsTemporarilyUnderpriced(configErrors) { lggr.Infow("Transaction temporarily underpriced", "err", sendError) - return commonclient.Successful + return multinode.Successful } if sendError.IsTerminallyUnderpriced(configErrors) { lggr.Errorw("Transaction terminally underpriced", "etx", tx, "err", sendError) - return commonclient.Underpriced + return multinode.Underpriced } if sendError.L2FeeTooLow(configErrors) || sendError.IsL2FeeTooHigh(configErrors) || sendError.IsL2Full(configErrors) { if isL2 { lggr.Errorw("Transaction fee out of range", "err", sendError, "etx", tx) - return commonclient.FeeOutOfValidRange + return multinode.FeeOutOfValidRange } lggr.Errorw("this error type only handled for L2s", "err", sendError, "etx", tx) - return commonclient.Unsupported + return multinode.Unsupported } if sendError.IsNonceTooHighError(configErrors) { // 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", sendError, "etx", tx) - return commonclient.Retryable + return multinode.Retryable } if sendError.IsInsufficientEth(configErrors) { 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, "etx", tx) - return commonclient.InsufficientFunds + return multinode.InsufficientFunds } if sendError.IsServiceUnavailable(configErrors) { lggr.Errorw(fmt.Sprintf("service unavailable while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx) - return commonclient.Retryable + return multinode.Retryable } if sendError.IsServiceTimeout(configErrors) { lggr.Errorw(fmt.Sprintf("service timed out while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx) - return commonclient.Retryable + return multinode.Retryable } if sendError.IsTimeout() { lggr.Errorw(fmt.Sprintf("timeout while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx) - return commonclient.Retryable + return multinode.Retryable } if sendError.IsCanceled() { lggr.Errorw(fmt.Sprintf("context was canceled while sending transaction %x", tx.Hash()), "err", sendError, "etx", tx) - return commonclient.Retryable + return multinode.Retryable } if sendError.IsTxFeeExceedsCap(configErrors) { lggr.Criticalw(fmt.Sprintf("Sending transaction failed: %s", label.RPCTxFeeCapConfiguredIncorrectlyWarning), @@ -654,15 +654,15 @@ func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger. "err", sendError, "id", "RPCTxFeeCapExceeded", ) - return commonclient.ExceedsMaxFee + return multinode.ExceedsMaxFee } if sendError.IsTerminallyStuckConfigError(configErrors) { lggr.Warnw("Transaction that would have been terminally stuck in the mempool detected on send. Marking as fatal error.", "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.TerminallyStuck + return multinode.TerminallyStuck } lggr.Criticalw("Unknown error encountered when sending transaction", "err", err, "etx", tx) - return commonclient.Unknown + return multinode.Unknown } var infura = ClientErrors{ diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go index 7ba042ab5c6..9a046922abb 100644 --- a/core/chains/evm/client/errors_test.go +++ b/core/chains/evm/client/errors_test.go @@ -9,7 +9,8 @@ import ( pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink-framework/multinode" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" ) @@ -255,9 +256,9 @@ func Test_Eth_Errors(t *testing.T) { assert.Equal(t, err.IsServiceUnavailable(clientErrors), test.expect) } { - err = evmclient.NewSendError(commonclient.ErroringNodeError) + err = evmclient.NewSendError(multinode.ErrNodeError) assert.True(t, err.IsServiceUnavailable(clientErrors)) - err = evmclient.NewSendError(fmt.Errorf("failed to send transaction: %w", commonclient.ErroringNodeError)) + err = evmclient.NewSendError(fmt.Errorf("failed to send transaction: %w", multinode.ErrNodeError)) assert.True(t, err.IsServiceUnavailable(clientErrors)) } }) diff --git a/core/chains/evm/client/evm_client.go b/core/chains/evm/client/evm_client.go index 18206265fd7..bf985bdfa28 100644 --- a/core/chains/evm/client/evm_client.go +++ b/core/chains/evm/client/evm_client.go @@ -6,6 +6,7 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-framework/multinode" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" @@ -13,22 +14,22 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" ) -func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, clientErrors evmconfig.ClientErrors, lggr logger.Logger, chainID *big.Int, nodes []*toml.Node, chainType chaintype.ChainType) (Client, error) { - var primaries []commonclient.Node[*big.Int, *RPCClient] - var sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient] +func NewEvmClient(cfg evmconfig.NodePool, chainCfg multinode.ChainConfig, clientErrors evmconfig.ClientErrors, lggr logger.Logger, chainID *big.Int, nodes []*toml.Node, chainType chaintype.ChainType) (Client, error) { + var primaries []multinode.Node[*big.Int, *RPCClient] + var sendonlys []multinode.SendOnlyNode[*big.Int, *RPCClient] largePayloadRPCTimeout, defaultRPCTimeout := getRPCTimeouts(chainType) for i, node := range nodes { if node.SendOnly != nil && *node.SendOnly { rpc := NewRPCClient(cfg, lggr, nil, node.HTTPURL.URL(), *node.Name, i, chainID, - commonclient.Secondary, largePayloadRPCTimeout, defaultRPCTimeout, chainType) - sendonly := commonclient.NewSendOnlyNode(lggr, (url.URL)(*node.HTTPURL), + multinode.Secondary, largePayloadRPCTimeout, defaultRPCTimeout, chainType) + sendonly := multinode.NewSendOnlyNode(lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID, rpc) sendonlys = append(sendonlys, sendonly) } else { rpc := NewRPCClient(cfg, lggr, node.WSURL.URL(), node.HTTPURL.URL(), *node.Name, i, - chainID, commonclient.Primary, largePayloadRPCTimeout, defaultRPCTimeout, chainType) - primaryNode := commonclient.NewNode(cfg, chainCfg, + chainID, multinode.Primary, largePayloadRPCTimeout, defaultRPCTimeout, chainType) + primaryNode := multinode.NewNode(cfg, chainCfg, lggr, node.WSURL.URL(), node.HTTPURL.URL(), *node.Name, i, chainID, *node.Order, rpc, "EVM") primaries = append(primaries, primaryNode) diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 6369c9dca12..2a68870e2e5 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -12,8 +12,9 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-framework/multinode" + "github.com/smartcontractkit/chainlink-framework/multinode/mocks" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -129,7 +130,7 @@ func (tc TestNodePoolConfig) DeathDeclarationDelay() time.Duration { func NewChainClientWithTestNode( t *testing.T, - nodeCfg commonclient.NodeConfig, + nodeCfg multinode.NodeConfig, noNewHeadsThreshold time.Duration, leaseDuration time.Duration, rpcUrl string, @@ -151,21 +152,21 @@ func NewChainClientWithTestNode( nodePoolCfg := TestNodePoolConfig{ NodeFinalizedBlockPollInterval: 1 * time.Second, } - rpc := NewRPCClient(nodePoolCfg, lggr, parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := NewRPCClient(nodePoolCfg, lggr, parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") - n := commonclient.NewNode[*big.Int, *evmtypes.Head, *RPCClient]( - nodeCfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM") - primaries := []commonclient.Node[*big.Int, *RPCClient]{n} + n := multinode.NewNode[*big.Int, *evmtypes.Head, *RPCClient]( + nodeCfg, mocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM") + primaries := []multinode.Node[*big.Int, *RPCClient]{n} - var sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient] + sendonlys := make([]multinode.SendOnlyNode[*big.Int, *RPCClient], len(sendonlyRPCURLs)) for i, u := range sendonlyRPCURLs { if u.Scheme != "http" && u.Scheme != "https" { return nil, pkgerrors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String()) } - rpc := NewRPCClient(nodePoolCfg, lggr, nil, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") - s := commonclient.NewSendOnlyNode[*big.Int, *RPCClient]( + rpc := NewRPCClient(nodePoolCfg, lggr, nil, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, multinode.Secondary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + s := multinode.NewSendOnlyNode[*big.Int, *RPCClient]( lggr, u, fmt.Sprintf("eth-sendonly-%d", i), chainID, rpc) - sendonlys = append(sendonlys, s) + sendonlys[i] = s } clientErrors := NewTestClientErrors() @@ -199,13 +200,13 @@ func NewChainClientWithMockedRpc( lggr := logger.Test(t) cfg := TestNodePoolConfig{ - NodeSelectionMode: commonclient.NodeSelectionModeRoundRobin, + NodeSelectionMode: multinode.NodeSelectionModeRoundRobin, } parsed, _ := url.ParseRequestURI("ws://test") - n := commonclient.NewNode[*big.Int, *evmtypes.Head, *RPCClient]( - cfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM") - primaries := []commonclient.Node[*big.Int, *RPCClient]{n} + n := multinode.NewNode[*big.Int, *evmtypes.Head, *RPCClient]( + cfg, mocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM") + primaries := []multinode.Node[*big.Int, *RPCClient]{n} clientErrors := NewTestClientErrors() c := NewChainClient(lggr, selectionMode, leaseDuration, primaries, nil, chainID, &clientErrors, 0, "") t.Cleanup(c.Close) diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go index b55c608a590..b712c297c18 100644 --- a/core/chains/evm/client/mocks/client.go +++ b/core/chains/evm/client/mocks/client.go @@ -11,8 +11,6 @@ import ( common "github.com/ethereum/go-ethereum/common" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - context "context" ethereum "github.com/ethereum/go-ethereum" @@ -21,6 +19,8 @@ import ( mock "github.com/stretchr/testify/mock" + multinode "github.com/smartcontractkit/chainlink-framework/multinode" + rpc "github.com/ethereum/go-ethereum/rpc" types "github.com/ethereum/go-ethereum/core/types" @@ -1628,22 +1628,22 @@ func (_c *Client_SendTransaction_Call) RunAndReturn(run func(context.Context, *t } // SendTransactionReturnCode provides a mock function with given fields: ctx, tx, fromAddress -func (_m *Client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { +func (_m *Client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (multinode.SendTxReturnCode, error) { ret := _m.Called(ctx, tx, fromAddress) if len(ret) == 0 { panic("no return value specified for SendTransactionReturnCode") } - var r0 commonclient.SendTxReturnCode + var r0 multinode.SendTxReturnCode var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) (commonclient.SendTxReturnCode, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) (multinode.SendTxReturnCode, error)); ok { return rf(ctx, tx, fromAddress) } - if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) commonclient.SendTxReturnCode); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) multinode.SendTxReturnCode); ok { r0 = rf(ctx, tx, fromAddress) } else { - r0 = ret.Get(0).(commonclient.SendTxReturnCode) + r0 = ret.Get(0).(multinode.SendTxReturnCode) } if rf, ok := ret.Get(1).(func(context.Context, *types.Transaction, common.Address) error); ok { @@ -1675,12 +1675,12 @@ func (_c *Client_SendTransactionReturnCode_Call) Run(run func(ctx context.Contex return _c } -func (_c *Client_SendTransactionReturnCode_Call) Return(_a0 commonclient.SendTxReturnCode, _a1 error) *Client_SendTransactionReturnCode_Call { +func (_c *Client_SendTransactionReturnCode_Call) Return(_a0 multinode.SendTxReturnCode, _a1 error) *Client_SendTransactionReturnCode_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *Client_SendTransactionReturnCode_Call) RunAndReturn(run func(context.Context, *types.Transaction, common.Address) (commonclient.SendTxReturnCode, error)) *Client_SendTransactionReturnCode_Call { +func (_c *Client_SendTransactionReturnCode_Call) RunAndReturn(run func(context.Context, *types.Transaction, common.Address) (multinode.SendTxReturnCode, error)) *Client_SendTransactionReturnCode_Call { _c.Call.Return(run) return _c } diff --git a/core/chains/evm/client/null_client.go b/core/chains/evm/client/null_client.go index b1dedd3f74a..6b40ba93eb7 100644 --- a/core/chains/evm/client/null_client.go +++ b/core/chains/evm/client/null_client.go @@ -11,8 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-framework/multinode" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) @@ -122,9 +122,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) (commonclient.SendTxReturnCode, error) { +func (nc *NullClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, sender common.Address) (multinode.SendTxReturnCode, error) { nc.lggr.Debug("SendTransactionReturnCode") - return commonclient.Successful, nil + return multinode.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 35d2a6dcd6b..f560a26dda6 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -26,9 +26,8 @@ import ( commonassets "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-framework/multinode" - 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" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" @@ -93,7 +92,7 @@ type RPCClient struct { name string id int chainID *big.Int - tier commonclient.NodeTier + tier multinode.NodeTier largePayloadRPCTimeout time.Duration finalizedBlockPollInterval time.Duration newHeadsPollInterval time.Duration @@ -118,13 +117,13 @@ type RPCClient struct { chainInfoLock sync.RWMutex // intercepted values seen by callers of the RPCClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee - highestUserObservations commonclient.ChainInfo + highestUserObservations multinode.ChainInfo // most recent chain info observed during current lifecycle (reseted on DisconnectAll) - latestChainInfo commonclient.ChainInfo + latestChainInfo multinode.ChainInfo } -var _ commonclient.RPCClient[*big.Int, *evmtypes.Head] = (*RPCClient)(nil) -var _ commonclient.SendTxRPCClient[*types.Transaction, *SendTxResult] = (*RPCClient)(nil) +var _ multinode.RPCClient[*big.Int, *evmtypes.Head] = (*RPCClient)(nil) +var _ multinode.SendTxRPCClient[*types.Transaction, *SendTxResult] = (*RPCClient)(nil) func NewRPCClient( cfg config.NodePool, @@ -134,7 +133,7 @@ func NewRPCClient( name string, id int, chainID *big.Int, - tier commonclient.NodeTier, + tier multinode.NodeTier, largePayloadRPCTimeout time.Duration, rpcTimeout time.Duration, chainType chaintype.ChainType, @@ -181,11 +180,11 @@ func (r *RPCClient) Ping(ctx context.Context) error { return err } -func (r *RPCClient) UnsubscribeAllExcept(subs ...commontypes.Subscription) { +func (r *RPCClient) UnsubscribeAllExcept(subs ...multinode.Subscription) { r.subsSliceMu.Lock() defer r.subsSliceMu.Unlock() - keepSubs := map[commontypes.Subscription]struct{}{} + keepSubs := map[multinode.Subscription]struct{}{} for _, sub := range subs { keepSubs[sub] = struct{}{} } @@ -265,7 +264,7 @@ func (r *RPCClient) Close() { r.cancelInflightRequests() r.UnsubscribeAllExcept() r.chainInfoLock.Lock() - r.latestChainInfo = commonclient.ChainInfo{} + r.latestChainInfo = multinode.ChainInfo{} r.chainInfoLock.Unlock() } @@ -452,7 +451,7 @@ func isRequestingFinalizedBlock(el rpc.BatchElem) bool { } } -func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.Head, sub commontypes.Subscription, err error) { +func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.Head, sub multinode.Subscription, err error) { ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() args := []interface{}{rpcSubscriptionMethodNewHeads} @@ -463,10 +462,10 @@ func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H if r.newHeadsPollInterval > 0 { interval := r.newHeadsPollInterval timeout := interval - isHealthCheckRequest := commonclient.CtxIsHeathCheckRequest(ctx) - poller, channel := commonclient.NewPoller[*evmtypes.Head](interval, func(ctx context.Context) (*evmtypes.Head, error) { + isHealthCheckRequest := multinode.CtxIsHeathCheckRequest(ctx) + poller, channel := multinode.NewPoller[*evmtypes.Head](interval, func(ctx context.Context) (*evmtypes.Head, error) { if isHealthCheckRequest { - ctx = commonclient.CtxAddHealthCheckFlag(ctx) + ctx = multinode.CtxAddHealthCheckFlag(ctx) } return r.latestBlock(ctx) }, timeout, r.rpcLog) @@ -514,7 +513,7 @@ func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H return channel, forwarder, err } -func (r *RPCClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, commontypes.Subscription, error) { +func (r *RPCClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, multinode.Subscription, error) { ctx, cancel, chStopInFlight, _, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() @@ -523,10 +522,10 @@ func (r *RPCClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmt return nil, nil, errors.New("FinalizedBlockPollInterval is 0") } timeout := interval - isHealthCheckRequest := commonclient.CtxIsHeathCheckRequest(ctx) - poller, channel := commonclient.NewPoller[*evmtypes.Head](interval, func(ctx context.Context) (*evmtypes.Head, error) { + isHealthCheckRequest := multinode.CtxIsHeathCheckRequest(ctx) + poller, channel := multinode.NewPoller[*evmtypes.Head](interval, func(ctx context.Context) (*evmtypes.Head, error) { if isHealthCheckRequest { - ctx = commonclient.CtxAddHealthCheckFlag(ctx) + ctx = multinode.CtxAddHealthCheckFlag(ctx) } return r.LatestFinalizedBlock(ctx) }, timeout, r.rpcLog) @@ -811,10 +810,10 @@ func (r *RPCClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (blo type SendTxResult struct { err error - code commonclient.SendTxReturnCode + code multinode.SendTxReturnCode } -var _ commonclient.SendTxResult = (*SendTxResult)(nil) +var _ multinode.SendTxResult = (*SendTxResult)(nil) func NewSendTxResult(err error) *SendTxResult { result := &SendTxResult{ @@ -827,7 +826,7 @@ func (r *SendTxResult) Error() error { return r.err } -func (r *SendTxResult) Code() commonclient.SendTxReturnCode { +func (r *SendTxResult) Code() multinode.SendTxReturnCode { return r.code } @@ -1418,9 +1417,9 @@ func (r *RPCClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, he r.chainInfoLock.Lock() defer r.chainInfoLock.Unlock() - if !commonclient.CtxIsHeathCheckRequest(ctx) { + if !multinode.CtxIsHeathCheckRequest(ctx) { r.highestUserObservations.BlockNumber = max(r.highestUserObservations.BlockNumber, head.Number) - r.highestUserObservations.TotalDifficulty = commonclient.MaxTotalDifficulty(r.highestUserObservations.TotalDifficulty, head.TotalDifficulty) + r.highestUserObservations.TotalDifficulty = multinode.MaxTotalDifficulty(r.highestUserObservations.TotalDifficulty, head.TotalDifficulty) } select { case <-requestCh: // no need to update latestChainInfo, as RPCClient already started new life cycle @@ -1437,7 +1436,7 @@ func (r *RPCClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan str } r.chainInfoLock.Lock() defer r.chainInfoLock.Unlock() - if !commonclient.CtxIsHeathCheckRequest(ctx) { + if !multinode.CtxIsHeathCheckRequest(ctx) { r.highestUserObservations.FinalizedBlockNumber = max(r.highestUserObservations.FinalizedBlockNumber, head.Number) } select { @@ -1448,7 +1447,7 @@ func (r *RPCClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan str } } -func (r *RPCClient) GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) { +func (r *RPCClient) GetInterceptedChainInfo() (latest, highestUserObservations multinode.ChainInfo) { r.chainInfoLock.Lock() defer r.chainInfoLock.Unlock() return r.latestChainInfo, r.highestUserObservations diff --git a/core/chains/evm/client/rpc_client_internal_test.go b/core/chains/evm/client/rpc_client_internal_test.go index ef321645fc2..ec1a89886cd 100644 --- a/core/chains/evm/client/rpc_client_internal_test.go +++ b/core/chains/evm/client/rpc_client_internal_test.go @@ -8,6 +8,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-framework/multinode" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -73,13 +74,13 @@ func TestRPCClient_MakeLogsValid(t *testing.T) { } for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { - rpc := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") log, err := rpc.makeLogValid(ethtypes.Log{TxIndex: tc.TxIndex, Index: tc.LogIndex}) // non sei should return as is require.NoError(t, err) require.Equal(t, tc.TxIndex, log.TxIndex) require.Equal(t, tc.LogIndex, log.Index) - seiRPC := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei) + seiRPC := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei) log, err = seiRPC.makeLogValid(ethtypes.Log{TxIndex: tc.TxIndex, Index: tc.LogIndex}) if tc.ExpectedError != nil { require.EqualError(t, err, tc.ExpectedError.Error()) diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index f6e7f9ee338..6fc02d3b2c1 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -25,8 +25,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-framework/multinode" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" @@ -74,7 +74,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { return } - checkClosedRPCClientShouldRemoveExistingSub := func(t tests.TestingT, ctx context.Context, sub commontypes.Subscription, rpcClient *client.RPCClient) { + checkClosedRPCClientShouldRemoveExistingSub := func(t tests.TestingT, ctx context.Context, sub multinode.Subscription, rpcClient *client.RPCClient) { errCh := sub.Err() rpcClient.UnsubscribeAllExcept() @@ -92,7 +92,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { t.Run("WS and HTTP URL cannot be both empty", func(t *testing.T) { // ws is optional when LogBroadcaster is disabled, however SubscribeFilterLogs will return error if ws is missing observedLggr := logger.Test(t) - rpcClient := client.NewRPCClient(nodePoolCfgHeadPolling, observedLggr, nil, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpcClient := client.NewRPCClient(nodePoolCfgHeadPolling, observedLggr, nil, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.Equal(t, errors.New("cannot dial rpc client when both ws and http info are missing"), rpcClient.Dial(ctx)) }) @@ -100,7 +100,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) // set to default values @@ -127,7 +127,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Equal(t, big.NewInt(500), latest.TotalDifficulty) - assertHighestUserObservations := func(highestUserObservations commonclient.ChainInfo) { + assertHighestUserObservations := func(highestUserObservations multinode.ChainInfo) { assert.Equal(t, int64(256), highestUserObservations.BlockNumber) assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) assert.Equal(t, big.NewInt(1000), highestUserObservations.TotalDifficulty) @@ -149,11 +149,11 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch, sub, err := rpc.SubscribeToHeads(commonclient.CtxAddHealthCheckFlag(tests.Context(t))) + ch, sub, err := rpc.SubscribeToHeads(multinode.CtxAddHealthCheckFlag(tests.Context(t))) require.NoError(t, err) defer sub.Unsubscribe() go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) @@ -192,7 +192,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { } server := createRPCServer() - rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, server.URL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) latest, highestUserObservations := rpc.GetInterceptedChainInfo() @@ -215,7 +215,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { // subscription with health check flag won't affect user observations sub.Unsubscribe() // stop prev subscription server.Head = &evmtypes.Head{Number: 256} - headCh, sub, err = rpc.SubscribeToHeads(commonclient.CtxAddHealthCheckFlag(tests.Context(t))) + headCh, sub, err = rpc.SubscribeToHeads(multinode.CtxAddHealthCheckFlag(tests.Context(t))) require.NoError(t, err) defer sub.Unsubscribe() @@ -231,7 +231,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) var wg sync.WaitGroup @@ -254,7 +254,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { t.Run("Block's chain ID matched configured", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) ch, sub, err := rpc.SubscribeToHeads(tests.Context(t)) @@ -270,7 +270,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { }) wsURL := server.WSURL() observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) - rpc := client.NewRPCClient(nodePoolCfgWSSub, observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgWSSub, observedLggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) server.Close() _, _, err := rpc.SubscribeToHeads(ctx) @@ -280,7 +280,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { t.Run("Closed rpc client should remove existing SubscribeToHeads subscription with WS", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -292,7 +292,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -304,7 +304,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -315,7 +315,7 @@ func TestRPCClient_SubscribeToHeads(t *testing.T) { t.Run("Subscription error is properly wrapper", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgWSSub, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) _, sub, err := rpc.SubscribeToHeads(ctx) @@ -345,7 +345,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { t.Run("Failed SubscribeFilterLogs when WSURL is empty", func(t *testing.T) { // ws is optional when LogBroadcaster is disabled, however SubscribeFilterLogs will return error if ws is missing observedLggr := logger.Test(t) - rpcClient := client.NewRPCClient(nodePoolCfg, observedLggr, nil, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpcClient := client.NewRPCClient(nodePoolCfg, observedLggr, nil, &url.URL{}, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.Nil(t, rpcClient.Dial(ctx)) _, err := rpcClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -357,7 +357,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { }) wsURL := server.WSURL() observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) - rpc := client.NewRPCClient(nodePoolCfg, observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, observedLggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) server.Close() _, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -374,7 +374,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { return resp }) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) sub, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -403,7 +403,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { return }) wsURL := server.WSURL() - rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei) + rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei) defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) ch := make(chan types.Log) @@ -498,7 +498,7 @@ func TestRPCClientFilterLogs(t *testing.T) { return }) wsURL := server.WSURL() - seiRPC := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei) + seiRPC := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei) defer seiRPC.Close() require.NoError(t, seiRPC.Dial(ctx)) logs, err := seiRPC.FilterLogs(ctx, ethereum.FilterQuery{}) @@ -508,7 +508,7 @@ func TestRPCClientFilterLogs(t *testing.T) { } // non sei should return index as is - rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainID, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) logs, err = rpc.FilterLogs(ctx, ethereum.FilterQuery{}) @@ -557,7 +557,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { } server := createRPCServer() - rpc := client.NewRPCClient(nodePoolCfg, lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, lggr, server.URL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) defer rpc.Close() server.Head.Store(&evmtypes.Head{Number: 128}) @@ -586,7 +586,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { // health check flg prevents change in highestUserObservations server.Head.Store(&evmtypes.Head{Number: 256}) - _, err = rpc.LatestFinalizedBlock(commonclient.CtxAddHealthCheckFlag(ctx)) + _, err = rpc.LatestFinalizedBlock(multinode.CtxAddHealthCheckFlag(ctx)) require.NoError(t, err) latest, highestUserObservations = rpc.GetInterceptedChainInfo() @@ -614,7 +614,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { // health check subscription only updates latest sub.Unsubscribe() // close previous one server.Head.Store(&evmtypes.Head{Number: 1024}) - ch, sub, err = rpc.SubscribeToFinalizedHeads(commonclient.CtxAddHealthCheckFlag(ctx)) + ch, sub, err = rpc.SubscribeToFinalizedHeads(multinode.CtxAddHealthCheckFlag(ctx)) require.NoError(t, err) defer sub.Unsubscribe() head = <-ch @@ -704,7 +704,7 @@ func TestRpcClientLargePayloadTimeout(t *testing.T) { // use something unreasonably large for RPC timeout to ensure that we use largePayloadRPCTimeout const rpcTimeout = time.Hour const largePayloadRPCTimeout = tests.TestInterval - rpc := client.NewRPCClient(nodePoolCfg, logger.Test(t), rpcURL, nil, "rpc", 1, chainId, commonclient.Primary, largePayloadRPCTimeout, rpcTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, logger.Test(t), rpcURL, nil, "rpc", 1, chainId, multinode.Primary, largePayloadRPCTimeout, rpcTimeout, "") require.NoError(t, rpc.Dial(ctx)) defer rpc.Close() err := testCase.Fn(ctx, rpc) @@ -749,7 +749,7 @@ func TestAstarCustomFinality(t *testing.T) { const expectedFinalizedBlockNumber = int64(4) const expectedFinalizedBlockHash = "0x7441e97acf83f555e0deefef86db636bc8a37eb84747603412884e4df4d22804" - rpcClient := client.NewRPCClient(nodePoolCfg, logger.Test(t), wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainAstar) + rpcClient := client.NewRPCClient(nodePoolCfg, logger.Test(t), wsURL, nil, "rpc", 1, chainId, multinode.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainAstar) defer rpcClient.Close() err := rpcClient.Dial(tests.Context(t)) require.NoError(t, err) diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index fd645203856..7b325e861a5 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -24,8 +24,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-framework/multinode" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -399,16 +399,16 @@ func (c *SimulatedBackendClient) HeaderByHash(ctx context.Context, h common.Hash return c.client.HeaderByHash(ctx, h) } -func (c *SimulatedBackendClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { +func (c *SimulatedBackendClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (multinode.SendTxReturnCode, error) { err := c.SendTransaction(ctx, tx) if err == nil { - return commonclient.Successful, nil + return multinode.Successful, nil } if strings.Contains(err.Error(), "could not fetch parent") || strings.Contains(err.Error(), "invalid transaction") { - return commonclient.Fatal, err + return multinode.Fatal, err } // All remaining error messages returned from SendTransaction are considered Unknown. - return commonclient.Unknown, err + return multinode.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 311f1aae648..4ed6729466b 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -27,8 +27,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-framework/multinode" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commmonfee "github.com/smartcontractkit/chainlink/v2/common/fee" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -261,7 +261,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Earlier tr := int32(99) @@ -289,7 +289,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Later laterEthTx := txmgr.Tx{ @@ -312,7 +312,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Insertion order deliberately reversed to test ordering require.NoError(t, txStore.InsertTx(ctx, &expensiveEthTx)) @@ -394,7 +394,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, []byte{42, 42, 0}, gasLimit, big.Int(assets.NewEthValue(242)), testutils.FixtureChainID) // Do the thing @@ -445,7 +445,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.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"]) @@ -478,7 +478,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.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() @@ -529,7 +529,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, []byte{42, 42, 0}, gasLimit, big.Int(assets.NewEthValue(243)), testutils.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(346) && tx.Value().Cmp(big.NewInt(243)) == 0 - }), fromAddress).Return(commonclient.Fatal, errors.New(terminallyStuckError)).Once() + }), fromAddress).Return(multinode.Fatal, errors.New(terminallyStuckError)).Once() // Start processing unstarted transactions retryable, err := eb.ProcessUnstartedTxs(tests.Context(t), fromAddress) @@ -569,7 +569,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, testutils.FixtureChainID, txRequestWithValue(big.Int(assets.NewEthValue(442))), @@ -592,7 +592,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, testutils.FixtureChainID, txRequestWithValue(big.Int(assets.NewEthValue(442))), @@ -713,7 +713,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() txRequest := txmgr.TxRequest{ FromAddress: fromAddress, @@ -799,7 +799,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Do the thing { @@ -836,7 +836,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(commonclient.Fatal, errors.New("exceeds block gas limit")).Once() + }), fromAddress).Return(multinode.Fatal, errors.New("exceeds block gas limit")).Once() // Do the thing { @@ -873,7 +873,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(commonclient.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once() + }), fromAddress).Return(multinode.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once() // Do the thing { @@ -909,7 +909,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(commonclient.TransactionAlreadyKnown, errors.New("nonce too low")).Once() + }), fromAddress).Return(multinode.TransactionAlreadyKnown, errors.New("nonce too low")).Once() // Do the thing { @@ -947,7 +947,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(commonclient.Retryable, failedToReachNodeError).Once() + }), fromAddress).Return(multinode.Retryable, failedToReachNodeError).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(tests.Context(t), fromAddress) @@ -994,7 +994,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(commonclient.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once() + }), fromAddress).Return(multinode.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once() // Do the thing { @@ -1059,7 +1059,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(commonclient.Successful, errors.New("replacement transaction underpriced")).Once() + }), fromAddress).Return(multinode.Successful, errors.New("replacement transaction underpriced")).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) @@ -1096,7 +1096,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.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() + }), fromAddress).Return(multinode.Fatal, errors.New(fatalErrorExample)).Once() retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) @@ -1146,7 +1146,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(commonclient.Fatal, errors.New(fatalErrorExample)).Once() + }), fromAddress).Return(multinode.Fatal, errors.New(fatalErrorExample)).Once() retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) @@ -1167,7 +1167,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(commonclient.Fatal, errors.New(fatalErrorExample)).Once() + }), fromAddress).Return(multinode.Fatal, errors.New(fatalErrorExample)).Once() { retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) @@ -1200,7 +1200,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.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() + }), fromAddress).Return(multinode.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 @@ -1258,7 +1258,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.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() + }), fromAddress).Return(multinode.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(localNextNonce, nil).Once() @@ -1284,7 +1284,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() retryable, err = eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) @@ -1310,7 +1310,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.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() + }), fromAddress).Return(multinode.Unknown, errors.New(retryableErrorExample)).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), errors.New("pending nonce fetch failed")).Once() // Do the thing @@ -1336,7 +1336,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() retryable, err = eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) @@ -1362,7 +1362,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.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() + }), fromAddress).Return(multinode.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(localNextNonce+1, nil).Once() @@ -1396,17 +1396,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(commonclient.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(multinode.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(commonclient.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(multinode.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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) @@ -1442,7 +1442,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(commonclient.Retryable, failedToReachNodeError).Once() + }), fromAddress).Return(multinode.Retryable, failedToReachNodeError).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) @@ -1473,7 +1473,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(commonclient.Successful, errors.New(temporarilyUnderpricedError)).Once() + }), fromAddress).Return(multinode.Successful, errors.New(temporarilyUnderpricedError)).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) @@ -1512,7 +1512,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(commonclient.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(multinode.Underpriced, errors.New(underpricedError)).Once() // Do the thing retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress) @@ -1530,7 +1530,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.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() + }), fromAddress).Return(multinode.InsufficientFunds, errors.New(insufficientEthError)).Once() retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) @@ -1560,7 +1560,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, testutils.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() + }), fromAddress).Return(multinode.Retryable, errors.New(nonceGapError)).Once() retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) @@ -1604,7 +1604,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { localNextNonce = getLocalNextNonce(t, nonceTracker, 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(commonclient.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(multinode.Underpriced, errors.New(underpricedError)).Once() // Check gas tip cap verification retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress) @@ -1635,15 +1635,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(commonclient.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(multinode.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(commonclient.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(multinode.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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress) require.NoError(t, err) @@ -1693,7 +1693,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_GasEstimationError(t *testing.T) ethClient.On("EstimateGas", mock.Anything, mock.Anything).Return(estimatedGasLimit, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(0) - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) @@ -1860,7 +1860,7 @@ func TestEthBroadcaster_NonceTracker_InProgressTx(t *testing.T) { inProgressTxNonce := uint64(0) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == inProgressTxNonce - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Tx with nonce 0 in DB will set local nonce map to value to 1 mustInsertInProgressEthTxWithAttempt(t, txStore, evmtypes.Nonce(inProgressTxNonce), fromAddress) @@ -1903,7 +1903,7 @@ func TestEthBroadcaster_HederaBroadcastValidation(t *testing.T) { localNonce := uint64(0) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNonce - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() ethClient.On("NonceAt", mock.Anything, fromAddress, mock.Anything).Return(uint64(1), nil).Once() mustInsertInProgressEthTxWithAttempt(t, txStore, evmtypes.Nonce(localNonce), fromAddress) @@ -1923,7 +1923,7 @@ func TestEthBroadcaster_HederaBroadcastValidation(t *testing.T) { localNonce := uint64(0) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNonce - }), fromAddress).Return(commonclient.Successful, nil).Twice() + }), fromAddress).Return(multinode.Successful, nil).Twice() ethClient.On("NonceAt", mock.Anything, fromAddress, mock.Anything).Return(uint64(0), nil).Once() ethClient.On("NonceAt", mock.Anything, fromAddress, mock.Anything).Return(uint64(1), nil).Once() @@ -1945,7 +1945,7 @@ func TestEthBroadcaster_HederaBroadcastValidation(t *testing.T) { localNonce := uint64(0) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNonce - }), fromAddress).Return(commonclient.Successful, nil).Times(4) + }), fromAddress).Return(multinode.Successful, nil).Times(4) ethClient.On("NonceAt", mock.Anything, fromAddress, mock.Anything).Return(uint64(0), nil).Times(4) etx := mustInsertInProgressEthTxWithAttempt(t, txStore, evmtypes.Nonce(localNonce), fromAddress) diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go index 9ec175048d3..adb70b8c9ef 100644 --- a/core/chains/evm/txmgr/client.go +++ b/core/chains/evm/txmgr/client.go @@ -15,8 +15,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-framework/multinode" - 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/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -48,14 +48,14 @@ func (c *evmTxmClient) BatchSendTransactions( batchSize int, lggr logger.SugaredLogger, ) ( - codes []commonclient.SendTxReturnCode, + codes []multinode.SendTxReturnCode, txErrs []error, broadcastTime time.Time, successfulTxIDs []int64, err error, ) { // preallocate - codes = make([]commonclient.SendTxReturnCode, len(attempts)) + codes = make([]multinode.SendTxReturnCode, len(attempts)) txErrs = make([]error, len(attempts)) reqs, broadcastTime, successfulTxIDs, batchErr := batchSendTransactions(ctx, attempts, batchSize, lggr, c.client) @@ -95,11 +95,11 @@ func (c *evmTxmClient) BatchSendTransactions( return } -func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.SugaredLogger) (commonclient.SendTxReturnCode, error) { +func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.SugaredLogger) (multinode.SendTxReturnCode, error) { signedTx, err := GetGethSignedTx(attempt.SignedRawTx) if err != nil { lggr.Criticalw("Fatal error signing transaction", "err", err, "etx", etx) - return commonclient.Fatal, err + return multinode.Fatal, err } return c.client.SendTransactionReturnCode(ctx, signedTx, etx.FromAddress) } diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index ea251971860..a35765272bb 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -21,8 +21,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-framework/multinode" - 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" @@ -751,7 +751,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(commonclient.Successful, nil).Once() + ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(multinode.Successful, nil).Once() err := ec.RebroadcastWhereNecessary(tests.Context(t), currentHead) require.NoError(t, err) @@ -800,7 +800,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(commonclient.Successful, nil).Once() + ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(multinode.Successful, nil).Once() err := ec.RebroadcastWhereNecessary(tests.Context(t), currentHead) require.NoError(t, err) @@ -862,7 +862,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_MaxFeeScenario(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.GasPrice().Int64() == int64(20000000000) && tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.ExceedsMaxFee, errors.New("tx fee (1.10 ether) exceeds the configured cap (1.00 ether)")).Once() + }), fromAddress).Return(multinode.ExceedsMaxFee, errors.New("tx fee (1.10 ether) exceeds the configured cap (1.00 ether)")).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead)) @@ -940,7 +940,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Fatal, errors.New("exceeds block gas limit")).Once() + }), fromAddress).Return(multinode.Fatal, errors.New("exceeds block gas limit")).Once() require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) var err error @@ -960,7 +960,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) var err error @@ -1003,7 +1003,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Successful, fmt.Errorf("known transaction: %s", etx.TxAttempts[0].Hash.Hex())).Once() + }), fromAddress).Return(multinode.Successful, fmt.Errorf("known transaction: %s", etx.TxAttempts[0].Hash.Hex())).Once() require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) var err error @@ -1030,7 +1030,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.TransactionAlreadyKnown, errors.New("nonce too low")).Once() + }), fromAddress).Return(multinode.TransactionAlreadyKnown, errors.New("nonce too low")).Once() require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) var err error @@ -1061,7 +1061,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Unknown, errors.New("some network error")).Once() + }), fromAddress).Return(multinode.Unknown, errors.New("some network error")).Once() err := ec.RebroadcastWhereNecessary(ctx, currentHead) require.Error(t, err) @@ -1085,7 +1085,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { // Try again and move the attempt into "broadcast" ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) @@ -1112,10 +1112,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Underpriced, errors.New("replacement transaction underpriced")).Once() + }), fromAddress).Return(multinode.Underpriced, errors.New("replacement transaction underpriced")).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) @@ -1148,7 +1148,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Underpriced, errors.New("underpriced")).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(multinode.Underpriced, errors.New("underpriced")).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.Error(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) @@ -1179,7 +1179,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Underpriced, errors.New("underpriced")).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(multinode.Underpriced, errors.New("underpriced")).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.Error(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) @@ -1210,7 +1210,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) etx, err = txStore.FindTxWithAttempts(ctx, etx.ID) require.NoError(t, err) @@ -1242,7 +1242,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Underpriced, errors.New("underpriced")).Once() + }), fromAddress).Return(multinode.Underpriced, errors.New("underpriced")).Once() require.Error(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) etx, err = txStore.FindTxWithAttempts(ctx, etx.ID) @@ -1273,10 +1273,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Underpriced, errors.New("replacement transaction underpriced")).Once() + }), fromAddress).Return(multinode.Underpriced, errors.New("replacement transaction underpriced")).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Do it require.NoError(t, ec.RebroadcastWhereNecessary(ctx, currentHead)) @@ -1330,10 +1330,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh // Fail the first time with terminally underpriced. ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - commonclient.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Once() + multinode.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( - commonclient.Successful, nil).Once() + multinode.Successful, nil).Once() kst.On("SignTx", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return( signedTx, nil, ).Once() @@ -1353,10 +1353,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh // Fail a few times with terminally underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - commonclient.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Times(3) + multinode.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( - commonclient.Successful, nil).Once() + multinode.Successful, nil).Once() signedLegacyTx := new(types.Transaction) kst.On("SignTx", mock.Anything, mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Type() == 0x0 && tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 @@ -1385,10 +1385,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh // Fail a few times with terminally underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - commonclient.Underpriced, errors.New("transaction underpriced")).Times(3) + multinode.Underpriced, errors.New("transaction underpriced")).Times(3) // Succeed the second time after bumping gas. ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - commonclient.Successful, nil).Once() + multinode.Successful, nil).Once() signedDxFeeTx := new(types.Transaction) kst.On("SignTx", mock.Anything, mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Type() == 0x2 && tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 @@ -1445,7 +1445,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(commonclient.InsufficientFunds, insufficientEthError).Once() + }), fromAddress).Return(multinode.InsufficientFunds, insufficientEthError).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead)) @@ -1471,7 +1471,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(commonclient.InsufficientFunds, insufficientEthError).Once() + }), fromAddress).Return(multinode.InsufficientFunds, insufficientEthError).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead)) @@ -1496,7 +1496,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(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead)) @@ -1528,7 +1528,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { 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() + }), fromAddress).Return(multinode.Successful, nil).Once() nonce++ } @@ -1576,11 +1576,11 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyStuckError(t *testing. // Return terminally stuck error on first rebroadcast ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.TerminallyStuck, errors.New(terminallyStuckError)).Once() + }), fromAddress).Return(multinode.TerminallyStuck, errors.New(terminallyStuckError)).Once() // Return successful for purge attempt ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) //nolint:gosec // disable G115 - }), fromAddress).Return(commonclient.Successful, nil).Once() + }), fromAddress).Return(multinode.Successful, nil).Once() // Start processing transactions for rebroadcast require.NoError(t, ec.RebroadcastWhereNecessary(tests.Context(t), currentHead)) @@ -1622,7 +1622,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { tx.Gas() == overrideGasLimit && reflect.DeepEqual(tx.Data(), etx1.EncodedPayload) && tx.To().String() == etx1.ToAddress.String() - }), mock.Anything).Return(commonclient.Successful, nil).Once() + }), mock.Anything).Return(multinode.Successful, nil).Once() require.NoError(t, ec.ForceRebroadcast(tests.Context(t), []evmtypes.Nonce{1}, gasPriceWei, fromAddress, overrideGasLimit)) }) @@ -1637,7 +1637,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { tx.Gas() == etx1.FeeLimit && reflect.DeepEqual(tx.Data(), etx1.EncodedPayload) && tx.To().String() == etx1.ToAddress.String() - }), mock.Anything).Return(commonclient.Successful, nil).Once() + }), mock.Anything).Return(multinode.Successful, nil).Once() require.NoError(t, ec.ForceRebroadcast(tests.Context(t), []evmtypes.Nonce{(1)}, gasPriceWei, fromAddress, 0)) }) @@ -1648,10 +1648,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.GasPrice.Int64() && tx.Gas() == overrideGasLimit - }), mock.Anything).Return(commonclient.Successful, nil).Once() + }), mock.Anything).Return(multinode.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.GasPrice.Int64() && tx.Gas() == overrideGasLimit - }), mock.Anything).Return(commonclient.Successful, nil).Once() + }), mock.Anything).Return(multinode.Successful, nil).Once() require.NoError(t, ec.ForceRebroadcast(tests.Context(t), []evmtypes.Nonce{(1), (2)}, gasPriceWei, fromAddress, overrideGasLimit)) }) @@ -1662,10 +1662,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(commonclient.Successful, nil).Once() + }), mock.Anything).Return(multinode.Successful, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(2) - }), mock.Anything).Return(commonclient.Successful, nil).Once() + }), mock.Anything).Return(multinode.Successful, nil).Once() for i := 3; i <= 5; i++ { nonce := i ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { @@ -1675,7 +1675,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { *tx.To() == fromAddress && tx.Value().Cmp(big.NewInt(0)) == 0 && len(tx.Data()) == 0 - }), mock.Anything).Return(commonclient.Successful, nil).Once() + }), mock.Anything).Return(multinode.Successful, nil).Once() } nonces := []evmtypes.Nonce{(1), (2), (3), (4), (5)} @@ -1688,7 +1688,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.GasPrice.Int64() && tx.Gas() == config.EVM().GasEstimator().LimitDefault() - }), mock.Anything).Return(commonclient.Successful, nil).Once() + }), mock.Anything).Return(multinode.Successful, nil).Once() require.NoError(t, ec.ForceRebroadcast(tests.Context(t), []evmtypes.Nonce{(0)}, gasPriceWei, fromAddress, 0)) }) @@ -1702,7 +1702,7 @@ func TestEthConfirmer_ProcessStuckTransactions(t *testing.T) { ethKeyStore := cltest.NewKeyStore(t, db).Eth() _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) ethClient := testutils.NewEthClientMockWithDefaultChain(t) - ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(commonclient.Successful, nil).Once() + ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(multinode.Successful, nil).Once() lggr := logger.Test(t) feeEstimator := gasmocks.NewEvmFeeEstimator(t) diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 7cdc8c21840..5c9449b2107 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -12,8 +12,8 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" pgcommon "github.com/smartcontractkit/chainlink-common/pkg/sqlutil/pg" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-framework/multinode" - "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/capabilities" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/cmd" @@ -337,7 +337,7 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) { n := i ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == n - }), mock.Anything).Once().Return(client.Successful, nil) + }), mock.Anything).Once().Return(multinode.Successful, nil) } assert.NoError(t, c.RebroadcastTransactions(ctx)) @@ -417,7 +417,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { n := i ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return uint(tx.Nonce()) == n - }), mock.Anything).Once().Return(client.Successful, nil) + }), mock.Anything).Once().Return(multinode.Successful, nil) } assert.NoError(t, c.RebroadcastTransactions(ctx)) @@ -469,7 +469,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(client.Successful, nil) + ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(multinode.Successful, nil) client := cmd.Shell{ Config: config, diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index a55c57cc9a2..85faad8e5fe 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -48,8 +48,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-framework/multinode" - "github.com/smartcontractkit/chainlink/v2/common/client" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -561,7 +561,7 @@ func NewEthMocksWithTransactionsOnBlocksAssertions(t testing.TB) *evmclimocks.Cl c.On("Dial", mock.Anything).Maybe().Return(nil) c.On("SubscribeToHeads", mock.Anything).Maybe().Return(chHead, EmptyMockSubscription(t), nil) c.On("SendTransaction", mock.Anything, mock.Anything).Maybe().Return(nil) - c.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(client.Successful, nil) + c.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(multinode.Successful, nil) // Construct chain h2 := Head(2) h1 := HeadWithHash(1, h2.ParentHash) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index a9e5130db2c..91924b9b681 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -37,7 +37,7 @@ require ( github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/umbracle/ethgo v0.1.3 github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 github.com/urfave/cli v1.22.14 @@ -277,13 +277,13 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect 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/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // 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.21.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/prometheus v0.54.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect @@ -296,7 +296,7 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sanity-io/litter v1.5.5 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect - github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // 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 @@ -307,6 +307,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect + github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 7b004d6eae7..b39eed9a91b 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1031,9 +1031,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -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/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -1071,8 +1070,8 @@ 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.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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= @@ -1125,8 +1124,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -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/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= @@ -1170,6 +1169,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030 github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU= github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM= github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ= @@ -1247,8 +1248,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F 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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 9a08b356c66..7d5b8628651 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -22,10 +22,10 @@ import ( commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" + "github.com/smartcontractkit/chainlink-framework/multinode" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" @@ -49,7 +49,7 @@ var ( second = *commoncfg.MustNewDuration(time.Second) minute = *commoncfg.MustNewDuration(time.Minute) - selectionMode = client.NodeSelectionModeHighestHead + selectionMode = multinode.NodeSelectionModeHighestHead multiChain = Config{ Core: toml.Core{ @@ -258,7 +258,7 @@ func TestConfig_Marshal(t *testing.T) { require.NoError(t, err) return &a } - selectionMode := client.NodeSelectionModeHighestHead + selectionMode := multinode.NodeSelectionModeHighestHead global := Config{ Core: toml.Core{ diff --git a/deployment/go.mod b/deployment/go.mod index 34fbc7fd21d..5261299679f 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -33,7 +33,7 @@ require ( github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/test-go/testify v1.1.4 github.com/testcontainers/testcontainers-go v0.34.0 go.uber.org/multierr v1.11.0 @@ -375,14 +375,14 @@ require ( 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 + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // 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.27.0 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/exporter-toolkit v0.11.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -397,7 +397,7 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sanity-io/litter v1.5.5 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect - github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/scylladb/go-reflectx v1.0.1 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/sercand/kuberesolver/v5 v5.1.1 // indirect @@ -410,6 +410,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect + github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect diff --git a/deployment/go.sum b/deployment/go.sum index a364fc9b1ec..5f3ef4e5776 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1273,9 +1273,8 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -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/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -1323,8 +1322,8 @@ 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.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= @@ -1380,8 +1379,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -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/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29 h1:BkTk4gynLjguayxrYxZoMZjBnAOh7ntQvUkOFmkMqPU= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= @@ -1435,6 +1434,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030 github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU= github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM= github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ= @@ -1522,8 +1523,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F 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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= diff --git a/go.md b/go.md index 9f51ecd4c81..c2d3e050b1e 100644 --- a/go.md +++ b/go.md @@ -37,6 +37,8 @@ flowchart LR click chainlink-data-streams href "https://github.com/smartcontractkit/chainlink-data-streams" chainlink-feeds --> chainlink-common click chainlink-feeds href "https://github.com/smartcontractkit/chainlink-feeds" + chainlink-framework/multinode --> chainlink-common + click chainlink-framework/multinode href "https://github.com/smartcontractkit/chainlink-framework" chainlink-protos/orchestrator --> wsrpc click chainlink-protos/orchestrator href "https://github.com/smartcontractkit/chainlink-protos" chainlink-solana --> chainlink-common @@ -48,6 +50,7 @@ flowchart LR chainlink/v2 --> chainlink-cosmos chainlink/v2 --> chainlink-data-streams chainlink/v2 --> chainlink-feeds + chainlink/v2 --> chainlink-framework/multinode chainlink/v2 --> chainlink-protos/orchestrator chainlink/v2 --> chainlink-solana chainlink/v2 --> chainlink-starknet/relayer @@ -114,6 +117,8 @@ flowchart LR click chainlink-data-streams href "https://github.com/smartcontractkit/chainlink-data-streams" chainlink-feeds --> chainlink-common click chainlink-feeds href "https://github.com/smartcontractkit/chainlink-feeds" + chainlink-framework/multinode --> chainlink-common + click chainlink-framework/multinode href "https://github.com/smartcontractkit/chainlink-framework" chainlink-protos/job-distributor click chainlink-protos/job-distributor href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/orchestrator --> wsrpc @@ -150,6 +155,7 @@ flowchart LR chainlink/v2 --> chainlink-cosmos chainlink/v2 --> chainlink-data-streams chainlink/v2 --> chainlink-feeds + chainlink/v2 --> chainlink-framework/multinode chainlink/v2 --> chainlink-protos/orchestrator chainlink/v2 --> chainlink-solana chainlink/v2 --> chainlink-starknet/relayer diff --git a/go.mod b/go.mod index a0b4bca1a87..c12790b12c7 100644 --- a/go.mod +++ b/go.mod @@ -68,7 +68,7 @@ require ( github.com/pressly/goose/v3 v3.21.1 github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.60.0 + github.com/prometheus/common v0.60.1 github.com/prometheus/prometheus v0.54.1 github.com/robfig/cron/v3 v3.0.1 github.com/rogpeppe/go-internal v1.13.1 @@ -83,6 +83,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 github.com/smartcontractkit/chainlink-feeds v0.1.1 + github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 @@ -91,7 +92,7 @@ require ( github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de github.com/smartcontractkit/wsrpc v0.8.2 github.com/spf13/cast v1.6.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/test-go/testify v1.1.4 github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a github.com/tidwall/gjson v1.17.0 @@ -305,7 +306,7 @@ require ( github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runc v1.1.10 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect @@ -318,7 +319,7 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sanity-io/litter v1.5.5 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect - github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect diff --git a/go.sum b/go.sum index 49520fd1845..c8778ab3532 100644 --- a/go.sum +++ b/go.sum @@ -1021,9 +1021,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -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/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -1061,8 +1060,8 @@ 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.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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= @@ -1116,8 +1115,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -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/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= @@ -1159,6 +1158,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030 github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU= github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ= github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce h1:Mvpbr/Fi2IdU2EcmqCxhlCzs5ncnx+BwV80YweA4DZs= @@ -1232,8 +1233,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F 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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 1c63ac6738e..e19f4dbe60d 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -38,7 +38,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/pelletier/go-toml/v2 v2.2.3 github.com/pkg/errors v0.9.1 - github.com/prometheus/common v0.60.0 + github.com/prometheus/common v0.60.1 github.com/rs/zerolog v1.33.0 github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 @@ -56,7 +56,7 @@ require ( github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.2 github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919 github.com/spf13/cobra v1.8.1 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/subosito/gotenv v1.6.0 github.com/test-go/testify v1.1.4 github.com/testcontainers/testcontainers-go v0.34.0 @@ -394,7 +394,7 @@ require ( 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 + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect @@ -415,7 +415,7 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sanity-io/litter v1.5.5 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect - github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/sercand/kuberesolver/v5 v5.1.1 // indirect github.com/sethvargo/go-retry v0.2.4 // indirect @@ -427,6 +427,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect + github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 1baff05f3f1..6c71b224f8e 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1290,9 +1290,8 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -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/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -1340,8 +1339,8 @@ 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.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= @@ -1397,8 +1396,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -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/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29 h1:BkTk4gynLjguayxrYxZoMZjBnAOh7ntQvUkOFmkMqPU= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= @@ -1456,6 +1455,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030 github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU= github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM= github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ= @@ -1545,8 +1546,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F 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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 04f9b07f93b..5c6be37328c 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -32,7 +32,7 @@ require ( github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.9 github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.2 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/wiremock/go-wiremock v1.9.0 go.uber.org/ratelimit v0.3.1 golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c @@ -371,14 +371,14 @@ require ( 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 + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // 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.27.0 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/exporter-toolkit v0.11.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -393,7 +393,7 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sanity-io/litter v1.5.5 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect - github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/scylladb/go-reflectx v1.0.1 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/segmentio/ksuid v1.0.4 // indirect @@ -411,6 +411,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect + github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241223151630-eac4f1508dce // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 48bdf1cb5b1..75a961d80a8 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1280,9 +1280,8 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -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/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -1330,8 +1329,8 @@ 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.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= @@ -1387,8 +1386,8 @@ github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQ github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -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/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29 h1:BkTk4gynLjguayxrYxZoMZjBnAOh7ntQvUkOFmkMqPU= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= @@ -1447,6 +1446,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030 github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3/go.mod h1:AS6zY2BkcRwfiGzNabGbHhfrLSrXrcI/GmjnT4jQ5/s= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20 h1:p/gzWpEf8alodCXm2Gtx2kWI/O9ZLdWZOdNnv5ZGO6c= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20241220173418-09e17ddbeb20/go.mod h1:4JqpgFy01LaqG1yM2iFTzwX3ZgcAvW9WdstBZQgPHzU= github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 h1:0ewLMbAz3rZrovdRUCgd028yOXX8KigB4FndAUdI2kM= github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/orchestrator v0.4.0 h1:ZBat8EBvE2LpSQR9U1gEbRV6PfAkiFdINmQ8nVnXIAQ= @@ -1536,8 +1537,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F 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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= From 5f5e552d587223bd817f7d0f62a6a2eda53a3079 Mon Sep 17 00:00:00 2001 From: dimitris Date: Thu, 9 Jan 2025 12:05:47 +0200 Subject: [PATCH 6/7] improve peer group dialer sync function logs (#15867) --- .../capabilities/ccip/oraclecreator/bootstrap.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/capabilities/ccip/oraclecreator/bootstrap.go b/core/capabilities/ccip/oraclecreator/bootstrap.go index 8dfe3e99ffb..40b3727640d 100644 --- a/core/capabilities/ccip/oraclecreator/bootstrap.go +++ b/core/capabilities/ccip/oraclecreator/bootstrap.go @@ -12,6 +12,7 @@ import ( "time" mapset "github.com/deckarep/golang-set/v2" + logger2 "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/smartcontractkit/libocr/networking" @@ -373,13 +374,22 @@ func (d *peerGroupDialer) sync() { defer d.mu.Unlock() activeDigest, candidateDigest := d.rmnHomeReader.GetAllConfigDigests() + + lggr := logger2.With( + d.lggr, + "method", "sync", + "activeDigest", activeDigest, + "candidateDigest", candidateDigest, + "activeConfigDigests", d.activeConfigDigests, + ) + actions := calculateSyncActions(d.activeConfigDigests, activeDigest, candidateDigest) if len(actions) == 0 { - d.lggr.Debugw("No peer group actions needed") + lggr.Debugw("No peer group actions needed") return } - d.lggr.Infow("Syncing peer groups", "actions", actions) + lggr.Infof("Syncing peer groups by applying the actions: %v", actions) // Handle each action for _, action := range actions { @@ -388,7 +398,7 @@ func (d *peerGroupDialer) sync() { d.closePeerGroup(action.configDigest) case ActionCreate: if err := d.createPeerGroup(action.configDigest); err != nil { - d.lggr.Errorw("Failed to create peer group", + lggr.Errorw("Failed to create peer group", "configDigest", action.configDigest, "err", err) // Consider closing all groups on error From 8270318279ab992328288973f2b831aa9440f6b1 Mon Sep 17 00:00:00 2001 From: Yashvardhan Nevatia Date: Thu, 9 Jan 2025 10:09:11 +0000 Subject: [PATCH 7/7] Solana devnet spin up in memory env (A) (#15831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding solchains in NewEnv * Revert "Adding solchains in NewEnv" This reverts commit aaab52e01412ea4f1de4f748cadf0c127682418c. * adding sol chains to newenv * newEnv needs to send nil * adding test env setup * adding nil for crib sol chains * adding chain selectors commit * go mod tidy * linting * chain sel update * update core/scripts go files * again * add changeset * go imports * go mod tidy * Update modgraph --------- Co-authored-by: Terry Tata Co-authored-by: Blaž Hrastnik --- .changeset/cuddly-turtles-arrive.md | 5 + core/scripts/go.mod | 24 ++-- core/scripts/go.sum | 125 +++---------------- deployment/environment.go | 13 ++ deployment/environment/crib/types.go | 1 + deployment/environment/devenv/environment.go | 1 + deployment/environment/memory/chain.go | 41 +++++- deployment/environment/memory/environment.go | 35 ++++++ deployment/go.mod | 23 ++-- deployment/go.sum | 118 +++-------------- deployment/solana_chain.go | 38 ++++++ go.md | 11 +- integration-tests/go.mod | 23 ++-- integration-tests/go.sum | 117 +++-------------- integration-tests/load/go.mod | 22 ++-- integration-tests/load/go.sum | 117 +++-------------- 16 files changed, 262 insertions(+), 452 deletions(-) create mode 100644 .changeset/cuddly-turtles-arrive.md diff --git a/.changeset/cuddly-turtles-arrive.md b/.changeset/cuddly-turtles-arrive.md new file mode 100644 index 00000000000..81ceed3e8ff --- /dev/null +++ b/.changeset/cuddly-turtles-arrive.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal adding solana devnet to ccip deployment diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 91924b9b681..e016559d6cf 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -48,7 +48,6 @@ require ( ) require ( - contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect @@ -118,8 +117,7 @@ require ( github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect @@ -138,8 +136,8 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gagliardetto/binary v0.7.7 // indirect - github.com/gagliardetto/solana-go v1.8.4 // indirect + github.com/gagliardetto/binary v0.8.0 // indirect + github.com/gagliardetto/solana-go v1.12.0 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect @@ -178,7 +176,6 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/glog v1.2.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect @@ -302,8 +299,9 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix // indirect - github.com/smartcontractkit/chain-selectors v1.0.34 // indirect + github.com/smartcontractkit/chain-selectors v1.0.36 // indirect github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 // indirect + github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect @@ -322,13 +320,12 @@ require ( github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 // indirect + github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect - github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect github.com/test-go/testify v1.1.4 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect github.com/tidwall/btree v1.6.0 // indirect @@ -355,7 +352,6 @@ require ( go.dedis.ch/kyber/v3 v3.1.0 // indirect go.etcd.io/bbolt v1.3.9 // indirect go.mongodb.org/mongo-driver v1.15.0 // indirect - go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect @@ -382,16 +378,16 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.32.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/tools v0.28.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.15.1 // indirect google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index b39eed9a91b..b716caa9ec3 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -3,7 +3,6 @@ cel.dev/expr v0.17.0/go.mod h1:HCwbrn+qQoHPXgfz6cl2J0hDybnX2N1sQQkl9EggXx8= 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.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= 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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= @@ -54,10 +53,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM= cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE= -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= -contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= @@ -73,7 +68,6 @@ cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -100,8 +94,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= -github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= -github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3 h1:cb3br57K508pQEFgBxn9GDhPS9HefpyMPK1RzmtMNzk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0= @@ -128,7 +120,6 @@ 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/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs= github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A= -github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 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= @@ -138,7 +129,6 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1L 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= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -156,8 +146,6 @@ github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c h1:cxQ github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c/go.mod h1:3XzxudkrYVUvbduN/uI2fl4lSrMSzU0+3RCu2mpnfx8= github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= -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.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI= github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= @@ -178,7 +166,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 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= @@ -267,16 +254,13 @@ github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7b github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -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= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 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.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= @@ -314,7 +298,6 @@ github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJF github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -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= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= @@ -328,23 +311,18 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= -github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= -github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= @@ -384,7 +362,6 @@ github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2 github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= 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.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= @@ -403,12 +380,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY= -github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= +github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= +github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY= -github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE= -github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8= +github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg= +github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok= github.com/gagliardetto/utilz v0.1.1 h1:/etW4hl607emKg6R6Lj9jRJ9d6ue2AQOyjhuAwjzs1U= @@ -526,7 +503,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -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= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -562,7 +538,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= @@ -638,12 +613,10 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= 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.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.2/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.1.2 h1:7vCfdORYQMCxIzI3NlYAs3FcBP760+gWuYWOyiVyYx8= @@ -654,15 +627,12 @@ github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EK github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 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.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= @@ -717,7 +687,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -807,10 +776,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= -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= @@ -821,7 +788,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -841,7 +807,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -883,7 +848,6 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 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.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -899,7 +863,6 @@ github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -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= @@ -908,8 +871,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -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= @@ -964,7 +925,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -983,7 +943,6 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -991,7 +950,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -1055,7 +1013,6 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om github.com/pressly/goose/v3 v3.21.1 h1:5SSAKKWej8LVVzNLuT6KIvP1eFDuPvxa+B6H0w78buQ= github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfWh8pHEe+vE= 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.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= @@ -1066,21 +1023,17 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -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.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ= github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY= -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= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= @@ -1094,7 +1047,6 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 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= @@ -1155,12 +1107,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix h1:DPJD++yKLSx0EfT+U14P8vLVxjXFmoIETiCO9lVwQo8= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= -github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= -github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPorGF//PAqwq2Cm4gRK0= +github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8= github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= @@ -1195,7 +1149,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1210,7 +1163,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -1220,15 +1172,13 @@ 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.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= 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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1260,22 +1210,17 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrjiCUizNCxI53bl/BnPiVwXqLzqYTqgU= github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA= 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.14.2/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= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -1285,7 +1230,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= @@ -1311,24 +1255,17 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= 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/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -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-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -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= @@ -1353,18 +1290,15 @@ go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1 go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= 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.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= 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= @@ -1436,15 +1370,12 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= @@ -1471,7 +1402,6 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y 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-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= @@ -1485,8 +1415,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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= 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= @@ -1515,15 +1445,14 @@ 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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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= @@ -1536,7 +1465,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL 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-20190813141303-74dc4d7220e7/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= @@ -1569,8 +1497,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug 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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= 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= @@ -1605,7 +1533,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1621,7 +1548,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1686,7 +1612,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.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= @@ -1731,7 +1656,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/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-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= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1741,7 +1665,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn 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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1777,8 +1700,8 @@ 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.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= 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= @@ -1794,7 +1717,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt 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.10.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= @@ -1819,7 +1741,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl 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.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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -1829,7 +1750,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 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= @@ -1878,9 +1798,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go. 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.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 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= @@ -1934,16 +1852,13 @@ gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 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.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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= diff --git a/deployment/environment.go b/deployment/environment.go index 6fc28fac764..a37622dc3ac 100644 --- a/deployment/environment.go +++ b/deployment/environment.go @@ -111,6 +111,7 @@ func NewEnvironment( logger logger.Logger, existingAddrs AddressBook, chains map[uint64]Chain, + solChains map[uint64]SolChain, nodeIDs []string, offchain OffchainClient, ctx func() context.Context, @@ -121,6 +122,7 @@ func NewEnvironment( Logger: logger, ExistingAddresses: existingAddrs, Chains: chains, + SolChains: solChains, NodeIDs: nodeIDs, Offchain: offchain, GetContext: ctx, @@ -159,6 +161,17 @@ func (e Environment) AllChainSelectorsExcluding(excluding []uint64) []uint64 { return selectors } +func (e Environment) AllChainSelectorsSolana() []uint64 { + selectors := make([]uint64, 0, len(e.SolChains)) + for sel := range e.SolChains { + selectors = append(selectors, sel) + } + sort.Slice(selectors, func(i, j int) bool { + return selectors[i] < selectors[j] + }) + return selectors +} + func (e Environment) AllDeployerKeys() []common.Address { var deployerKeys []common.Address for sel := range e.Chains { diff --git a/deployment/environment/crib/types.go b/deployment/environment/crib/types.go index 99baf8e8774..771880053f9 100644 --- a/deployment/environment/crib/types.go +++ b/deployment/environment/crib/types.go @@ -33,6 +33,7 @@ func NewDeployEnvironmentFromCribOutput(lggr logger.Logger, output DeployOutput) lggr, output.AddressBook, chains, + nil, // nil for solana chains, can use memory solana chain example when required output.NodeIDs, nil, // todo: populate the offchain client using output.DON func() context.Context { return context.Background() }, deployment.XXXGenerateTestOCRSecrets(), diff --git a/deployment/environment/devenv/environment.go b/deployment/environment/devenv/environment.go index 2fffe6adf2b..b6b5198f8fb 100644 --- a/deployment/environment/devenv/environment.go +++ b/deployment/environment/devenv/environment.go @@ -50,6 +50,7 @@ func NewEnvironment(ctx func() context.Context, lggr logger.Logger, config Envir lggr, deployment.NewMemoryAddressBook(), chains, + nil, // sending nil for solana chains right now, we can build this when we need it nodeIDs, offChain, ctx, diff --git a/deployment/environment/memory/chain.go b/deployment/environment/memory/chain.go index 77a8f397d39..cc22b40d844 100644 --- a/deployment/environment/memory/chain.go +++ b/deployment/environment/memory/chain.go @@ -9,12 +9,16 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient/simulated" + "github.com/gagliardetto/solana-go" + solRpc "github.com/gagliardetto/solana-go/rpc" + "github.com/stretchr/testify/require" + solTestUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" + chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" ) @@ -24,6 +28,11 @@ type EVMChain struct { Users []*bind.TransactOpts } +type SolanaChain struct { + Client *solRpc.Client + DeployerKey *solana.PrivateKey +} + func fundAddress(t *testing.T, from *bind.TransactOpts, to common.Address, amount *big.Int, backend *simulated.Backend) { ctx := tests.Context(t) nonce, err := backend.Client().PendingNonceAt(ctx, from.From) @@ -53,6 +62,36 @@ func GenerateChains(t *testing.T, numChains int, numUsers int) map[uint64]EVMCha return chains } +func getTestSolanaChainSelectors() []uint64 { + result := []uint64{} + for _, x := range chainsel.SolanaALL { + if x.Name == x.ChainID { + result = append(result, x.Selector) + } + } + return result +} + +func GenerateChainsSol(t *testing.T, numChains int) map[uint64]SolanaChain { + chains := make(map[uint64]SolanaChain) + testSolanaChainSelectors := getTestSolanaChainSelectors() + if len(testSolanaChainSelectors) < numChains { + t.Fatalf("not enough test solana chain selectors available") + } + + for i := 0; i < numChains; i++ { + chainID := testSolanaChainSelectors[i] + url, _ := solTestUtil.SetupLocalSolNodeWithFlags(t) + admin, gerr := solana.NewRandomPrivateKey() + require.NoError(t, gerr) + chains[chainID] = SolanaChain{ + Client: solRpc.New(url), + DeployerKey: &admin, + } + } + return chains +} + func GenerateChainsWithIds(t *testing.T, chainIDs []uint64, numUsers int) map[uint64]EVMChain { chains := make(map[uint64]EVMChain) for _, chainID := range chainIDs { diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go index a74d23a847b..3c5fdc6e779 100644 --- a/deployment/environment/memory/environment.go +++ b/deployment/environment/memory/environment.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core/types" + "github.com/gagliardetto/solana-go" "github.com/hashicorp/consul/sdk/freeport" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" @@ -19,6 +20,9 @@ import ( "github.com/smartcontractkit/chainlink/deployment" + solRpc "github.com/gagliardetto/solana-go/rpc" + + solCommomUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" "github.com/smartcontractkit/chainlink-common/pkg/logger" ) @@ -28,6 +32,7 @@ const ( type MemoryEnvironmentConfig struct { Chains int + SolChains int NumOfUsersPerChain int Nodes int Bootstraps int @@ -59,6 +64,11 @@ func NewMemoryChains(t *testing.T, numChains int, numUsers int) (map[uint64]depl return generateMemoryChain(t, mchains), users } +func NewMemoryChainsSol(t *testing.T, numChains int) map[uint64]deployment.SolChain { + mchains := GenerateChainsSol(t, numChains) + return generateMemoryChainSol(t, mchains) +} + func NewMemoryChainsWithChainIDs(t *testing.T, chainIDs []uint64, numUsers int) (map[uint64]deployment.Chain, map[uint64][]*bind.TransactOpts) { mchains := GenerateChainsWithIds(t, chainIDs, numUsers) users := make(map[uint64][]*bind.TransactOpts) @@ -111,6 +121,28 @@ func generateMemoryChain(t *testing.T, inputs map[uint64]EVMChain) map[uint64]de return chains } +func generateMemoryChainSol(t *testing.T, inputs map[uint64]SolanaChain) map[uint64]deployment.SolChain { + chains := make(map[uint64]deployment.SolChain) + for cid, chain := range inputs { + chain := chain + chains[cid] = deployment.SolChain{ + Selector: cid, + Client: chain.Client, + DeployerKey: chain.DeployerKey, + Confirm: func(instructions []solana.Instruction, opts ...solCommomUtil.TxModifier) error { + _, err := solCommomUtil.SendAndConfirm( + context.Background(), chain.Client, instructions, *chain.DeployerKey, solRpc.CommitmentConfirmed, opts..., + ) + if err != nil { + return err + } + return nil + }, + } + } + return chains +} + func NewNodes(t *testing.T, logLevel zapcore.Level, chains map[uint64]deployment.Chain, numNodes, numBootstraps int, registryConfig deployment.CapabilityRegistryConfig) map[string]Node { nodesByPeerID := make(map[string]Node) if numNodes+numBootstraps == 0 { @@ -149,6 +181,7 @@ func NewMemoryEnvironmentFromChainsNodes( lggr, deployment.NewMemoryAddressBook(), chains, + nil, nodeIDs, // Note these have the p2p_ prefix. NewMemoryJobClient(nodes), ctx, @@ -159,6 +192,7 @@ func NewMemoryEnvironmentFromChainsNodes( // To be used by tests and any kind of deployment logic. func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, logLevel zapcore.Level, config MemoryEnvironmentConfig) deployment.Environment { chains, _ := NewMemoryChains(t, config.Chains, config.NumOfUsersPerChain) + solChains := NewMemoryChainsSol(t, config.SolChains) nodes := NewNodes(t, logLevel, chains, config.Nodes, config.Bootstraps, config.RegistryConfig) var nodeIDs []string for id := range nodes { @@ -169,6 +203,7 @@ func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, logLevel zapcore.Lev lggr, deployment.NewMemoryAddressBook(), chains, + solChains, nodeIDs, NewMemoryJobClient(nodes), func() context.Context { return tests.Context(t) }, diff --git a/deployment/go.mod b/deployment/go.mod index 5261299679f..cbec5d95744 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -18,6 +18,7 @@ require ( github.com/aws/aws-sdk-go v1.54.19 github.com/deckarep/golang-set/v2 v2.6.0 github.com/ethereum/go-ethereum v1.14.11 + github.com/gagliardetto/solana-go v1.12.0 github.com/go-resty/resty/v2 v2.15.3 github.com/google/uuid v1.6.0 github.com/hashicorp/consul/sdk v0.16.1 @@ -27,8 +28,9 @@ require ( github.com/rs/zerolog v1.33.0 github.com/sethvargo/go-retry v0.2.4 github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix - github.com/smartcontractkit/chain-selectors v1.0.34 + github.com/smartcontractkit/chain-selectors v1.0.36 github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 + github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 @@ -38,7 +40,7 @@ require ( github.com/testcontainers/testcontainers-go v0.34.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 - golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c + golang.org/x/exp v0.0.0-20241210194714-1829a127f884 golang.org/x/oauth2 v0.23.0 golang.org/x/sync v0.10.0 google.golang.org/grpc v1.67.1 @@ -48,7 +50,6 @@ require ( ) require ( - contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect @@ -160,9 +161,8 @@ require ( github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dennwc/varint v1.0.0 // indirect - github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect @@ -189,8 +189,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gagliardetto/binary v0.7.7 // indirect - github.com/gagliardetto/solana-go v1.8.4 // indirect + github.com/gagliardetto/binary v0.8.0 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect @@ -430,13 +429,12 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 // indirect + github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect - 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.17.0 // indirect @@ -465,7 +463,6 @@ require ( go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect go.etcd.io/etcd/client/v3 v3.5.14 // indirect go.mongodb.org/mongo-driver v1.15.0 // indirect - go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector/pdata v1.12.0 // indirect go.opentelemetry.io/collector/semconv v0.105.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect @@ -495,13 +492,13 @@ require ( go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.32.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/tools v0.28.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect gonum.org/v1/gonum v0.15.1 // indirect diff --git a/deployment/go.sum b/deployment/go.sum index 5f3ef4e5776..e58794f2e97 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -3,7 +3,6 @@ cel.dev/expr v0.17.0/go.mod h1:HCwbrn+qQoHPXgfz6cl2J0hDybnX2N1sQQkl9EggXx8= 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.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= 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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= @@ -54,10 +53,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM= cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE= -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= -contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= @@ -75,7 +70,6 @@ cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8 dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -118,8 +112,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= -github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= -github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3 h1:cb3br57K508pQEFgBxn9GDhPS9HefpyMPK1RzmtMNzk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0= @@ -159,7 +151,6 @@ github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs= github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= -github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 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= @@ -181,7 +172,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u 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= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -210,8 +200,6 @@ github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinR github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= -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.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI= github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= @@ -269,7 +257,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= @@ -376,9 +363,7 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -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= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -389,7 +374,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.3.2/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= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= @@ -430,7 +414,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -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= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= @@ -444,27 +427,22 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= -github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= -github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4= @@ -539,12 +517,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY= -github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= +github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= +github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY= -github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE= -github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8= +github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg= +github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok= github.com/gagliardetto/utilz v0.1.1 h1:/etW4hl607emKg6R6Lj9jRJ9d6ue2AQOyjhuAwjzs1U= @@ -670,7 +648,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -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= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -706,7 +683,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= @@ -790,12 +766,10 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= 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.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.2/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/dskit v0.0.0-20231120170505-765e343eda4f h1:gyojr97YeWZ70pKNakWv5/tKwBHuLy3icnIeCo9gQr4= @@ -820,15 +794,12 @@ github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMN github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= 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.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.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= @@ -901,7 +872,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -1004,10 +974,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= -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= @@ -1018,7 +986,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -1043,7 +1010,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -1093,7 +1059,6 @@ github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/z github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 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.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -1194,7 +1159,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -1215,7 +1179,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -1302,7 +1265,6 @@ github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfW github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= 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= @@ -1315,8 +1277,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -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.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -1329,7 +1289,6 @@ github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57J github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= 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= @@ -1338,7 +1297,6 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ= github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY= -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= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= @@ -1350,7 +1308,6 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -1420,12 +1377,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix h1:DPJD++yKLSx0EfT+U14P8vLVxjXFmoIETiCO9lVwQo8= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= -github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= -github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPorGF//PAqwq2Cm4gRK0= +github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8= github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= @@ -1466,7 +1425,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= -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/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= @@ -1485,7 +1443,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -1495,15 +1452,13 @@ 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.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= 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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1535,9 +1490,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= @@ -1546,12 +1498,10 @@ github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrj github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA= 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.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= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -1559,7 +1509,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= @@ -1586,10 +1535,8 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8= github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= @@ -1598,10 +1545,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= 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/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -1609,7 +1552,6 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -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= @@ -1636,7 +1578,6 @@ go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1 go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -1648,12 +1589,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSv go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= -go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= 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.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= 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= @@ -1727,15 +1666,12 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= @@ -1765,7 +1701,6 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= @@ -1780,8 +1715,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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= 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= @@ -1810,15 +1745,14 @@ 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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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= @@ -1869,8 +1803,8 @@ 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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= 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= @@ -1906,7 +1840,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1922,7 +1855,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1998,7 +1930,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.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.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -2047,7 +1978,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-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= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -2057,7 +1987,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn 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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -2093,8 +2022,8 @@ 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.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= 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= @@ -2112,7 +2041,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt 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.10.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= @@ -2137,7 +2065,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl 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.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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -2148,7 +2075,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 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= @@ -2198,7 +2124,6 @@ google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE 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.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 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= @@ -2251,16 +2176,13 @@ gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 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.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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= diff --git a/deployment/solana_chain.go b/deployment/solana_chain.go index 338642e3e32..ba02e74f892 100644 --- a/deployment/solana_chain.go +++ b/deployment/solana_chain.go @@ -1,5 +1,43 @@ package deployment +import ( + "fmt" + "strconv" + + "github.com/gagliardetto/solana-go" + solRpc "github.com/gagliardetto/solana-go/rpc" + + solCommomUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" +) + // SolChain represents a Solana chain. type SolChain struct { + // Selectors used as canonical chain identifier. + Selector uint64 + // RPC cient + Client *solRpc.Client + // TODO: raw private key for now, need to replace with a more secure way + DeployerKey *solana.PrivateKey + Confirm func(instructions []solana.Instruction, opts ...solCommomUtil.TxModifier) error +} + +func (c SolChain) String() string { + chainInfo, err := ChainInfo(c.Selector) + if err != nil { + // we should never get here, if the selector is invalid it should not be in the environment + panic(err) + } + return fmt.Sprintf("%s (%d)", chainInfo.ChainName, chainInfo.ChainSelector) +} + +func (c SolChain) Name() string { + chainInfo, err := ChainInfo(c.Selector) + if err != nil { + // we should never get here, if the selector is invalid it should not be in the environment + panic(err) + } + if chainInfo.ChainName == "" { + return strconv.FormatUint(c.Selector, 10) + } + return chainInfo.ChainName } diff --git a/go.md b/go.md index c2d3e050b1e..ed41edee2b0 100644 --- a/go.md +++ b/go.md @@ -108,6 +108,8 @@ flowchart LR chainlink-ccip --> chain-selectors chainlink-ccip --> chainlink-common click chainlink-ccip href "https://github.com/smartcontractkit/chainlink-ccip" + chainlink-ccip/chains/solana --> chainlink-common + click chainlink-ccip/chains/solana href "https://github.com/smartcontractkit/chainlink-ccip" chainlink-common --> grpc-proxy chainlink-common --> libocr click chainlink-common href "https://github.com/smartcontractkit/chainlink-common" @@ -141,6 +143,7 @@ flowchart LR chainlink/core/scripts --> chainlink/deployment click chainlink/core/scripts href "https://github.com/smartcontractkit/chainlink" chainlink/deployment --> ccip-owner-contracts + chainlink/deployment --> chainlink-ccip/chains/solana chainlink/deployment --> chainlink-protos/job-distributor chainlink/deployment --> chainlink-testing-framework/lib chainlink/deployment --> chainlink/v2 @@ -184,6 +187,12 @@ flowchart LR end click chainlink-repo href "https://github.com/smartcontractkit/chainlink" + subgraph chainlink-ccip-repo[chainlink-ccip] + chainlink-ccip + chainlink-ccip/chains/solana + end + click chainlink-ccip-repo href "https://github.com/smartcontractkit/chainlink-ccip" + subgraph chainlink-protos-repo[chainlink-protos] chainlink-protos/job-distributor chainlink-protos/orchestrator @@ -206,5 +215,5 @@ flowchart LR click tdh2-repo href "https://github.com/smartcontractkit/tdh2" classDef outline stroke-dasharray:6,fill:none; - class chainlink-repo,chainlink-protos-repo,chainlink-testing-framework-repo,tdh2-repo outline + class chainlink-repo,chainlink-ccip-repo,chainlink-protos-repo,chainlink-testing-framework-repo,tdh2-repo outline ``` diff --git a/integration-tests/go.mod b/integration-tests/go.mod index e19f4dbe60d..efd538007b1 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -44,7 +44,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/shopspring/decimal v1.4.0 github.com/slack-go/slack v0.15.0 - github.com/smartcontractkit/chain-selectors v1.0.34 + github.com/smartcontractkit/chain-selectors v1.0.36 github.com/smartcontractkit/chainlink-automation v0.8.1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 @@ -65,7 +65,7 @@ require ( go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.31.0 - golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c + golang.org/x/exp v0.0.0-20241210194714-1829a127f884 golang.org/x/sync v0.10.0 golang.org/x/text v0.21.0 google.golang.org/grpc v1.67.1 @@ -74,7 +74,6 @@ require ( ) require ( - contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect @@ -183,9 +182,8 @@ require ( github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dennwc/varint v1.0.0 // indirect - github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect @@ -211,8 +209,8 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gagliardetto/binary v0.7.7 // indirect - github.com/gagliardetto/solana-go v1.8.4 // indirect + github.com/gagliardetto/binary v0.8.0 // indirect + github.com/gagliardetto/solana-go v1.12.0 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect @@ -424,6 +422,7 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix // indirect + github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241216163550-fa030d178ba3 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect @@ -443,12 +442,11 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 // indirect + github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect - 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.17.0 // indirect @@ -479,7 +477,6 @@ require ( go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect go.etcd.io/etcd/client/v3 v3.5.14 // indirect go.mongodb.org/mongo-driver v1.15.0 // indirect - go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector/pdata v1.12.0 // indirect go.opentelemetry.io/collector/semconv v0.105.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect @@ -507,13 +504,13 @@ require ( go.uber.org/ratelimit v0.3.1 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.11.0 // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.32.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/tools v0.28.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect gonum.org/v1/gonum v0.15.1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 6c71b224f8e..a2f198d4856 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -3,7 +3,6 @@ cel.dev/expr v0.17.0/go.mod h1:HCwbrn+qQoHPXgfz6cl2J0hDybnX2N1sQQkl9EggXx8= 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.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= 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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= @@ -54,10 +53,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM= cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE= -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= -contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= @@ -75,7 +70,6 @@ cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8 dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -118,8 +112,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= -github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= -github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0= @@ -159,7 +151,6 @@ github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs= github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A= -github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 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= @@ -177,7 +168,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u 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= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -204,8 +194,6 @@ github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinR github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= -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.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI= github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= @@ -263,7 +251,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= @@ -380,9 +367,7 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -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= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -393,7 +378,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.3.2/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= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= @@ -434,7 +418,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -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= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= @@ -448,27 +431,22 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= -github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= -github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4= github.com/digitalocean/godo v1.118.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -541,12 +519,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY= -github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= +github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= +github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY= -github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE= -github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8= +github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg= +github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok= github.com/gagliardetto/utilz v0.1.1 h1:/etW4hl607emKg6R6Lj9jRJ9d6ue2AQOyjhuAwjzs1U= @@ -674,7 +652,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -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= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -710,7 +687,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= @@ -795,7 +771,6 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= 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.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= @@ -825,15 +800,12 @@ github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMN github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= 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.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.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= @@ -906,7 +878,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -1011,10 +982,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= -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= @@ -1025,7 +994,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -1050,7 +1018,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -1102,7 +1069,6 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 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.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -1203,7 +1169,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -1228,7 +1193,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -1319,7 +1283,6 @@ github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfW github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= 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= @@ -1332,8 +1295,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -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.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -1346,7 +1307,6 @@ github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57J github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= 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= @@ -1355,7 +1315,6 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ= github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY= -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= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= @@ -1367,7 +1326,6 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -1441,12 +1399,14 @@ github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0 github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix h1:DPJD++yKLSx0EfT+U14P8vLVxjXFmoIETiCO9lVwQo8= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= -github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= -github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPorGF//PAqwq2Cm4gRK0= +github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8= github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= @@ -1489,7 +1449,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= -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/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= @@ -1508,7 +1467,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -1518,15 +1476,13 @@ 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.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= 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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1558,9 +1514,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= @@ -1571,12 +1524,10 @@ github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:Buzhfgf github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= 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.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= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -1584,7 +1535,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= @@ -1611,10 +1561,8 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8= github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= @@ -1623,10 +1571,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= 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/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -1634,7 +1578,6 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -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= @@ -1661,7 +1604,6 @@ go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1 go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -1673,12 +1615,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSv go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= -go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= 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.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= 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= @@ -1752,15 +1692,12 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= @@ -1791,7 +1728,6 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= @@ -1806,8 +1742,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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= 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= @@ -1836,15 +1772,14 @@ 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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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= @@ -1895,8 +1830,8 @@ 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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= 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= @@ -1932,7 +1867,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1949,7 +1883,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2026,7 +1959,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.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.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -2075,7 +2007,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-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= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -2085,7 +2016,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn 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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -2121,8 +2051,8 @@ 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.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= 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= @@ -2140,7 +2070,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt 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.10.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= @@ -2165,7 +2094,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl 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.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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -2176,7 +2104,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 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= @@ -2226,7 +2153,6 @@ google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE 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.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 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= @@ -2279,16 +2205,13 @@ gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 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.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 5c6be37328c..94d86cb5cd4 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -35,11 +35,10 @@ require ( github.com/stretchr/testify v1.10.0 github.com/wiremock/go-wiremock v1.9.0 go.uber.org/ratelimit v0.3.1 - golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c + golang.org/x/exp v0.0.0-20241210194714-1829a127f884 ) require ( - contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect @@ -153,9 +152,8 @@ require ( github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dennwc/varint v1.0.0 // indirect - github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect @@ -182,8 +180,8 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gagliardetto/binary v0.7.7 // indirect - github.com/gagliardetto/solana-go v1.8.4 // indirect + github.com/gagliardetto/binary v0.8.0 // indirect + github.com/gagliardetto/solana-go v1.12.0 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect @@ -405,7 +403,7 @@ require ( github.com/shoenig/test v0.6.6 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/smartcontractkit/chain-selectors v1.0.34 // indirect + github.com/smartcontractkit/chain-selectors v1.0.36 // indirect github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect @@ -430,13 +428,12 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 // indirect + github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect - github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect github.com/test-go/testify v1.1.4 // indirect github.com/testcontainers/testcontainers-go v0.34.0 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect @@ -470,7 +467,6 @@ require ( go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect go.etcd.io/etcd/client/v3 v3.5.14 // indirect go.mongodb.org/mongo-driver v1.15.0 // indirect - go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector/pdata v1.12.0 // indirect go.opentelemetry.io/collector/semconv v0.105.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect @@ -501,15 +497,15 @@ require ( go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.32.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/tools v0.28.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect gonum.org/v1/gonum v0.15.1 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 75a961d80a8..eb114fa0e28 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -3,7 +3,6 @@ cel.dev/expr v0.17.0/go.mod h1:HCwbrn+qQoHPXgfz6cl2J0hDybnX2N1sQQkl9EggXx8= 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.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= 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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= @@ -54,10 +53,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM= cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE= -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= -contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= @@ -75,7 +70,6 @@ cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8 dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -118,8 +112,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= -github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= -github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0= @@ -163,7 +155,6 @@ github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs= github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A= -github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 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= @@ -181,7 +172,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u 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= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -208,8 +198,6 @@ github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinR github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= -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.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI= github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= @@ -267,7 +255,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= @@ -374,9 +361,7 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -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= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -387,7 +372,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.3.2/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= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= @@ -428,7 +412,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -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= github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= @@ -442,27 +425,22 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= -github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= -github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4= github.com/digitalocean/godo v1.118.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -535,12 +513,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY= -github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= +github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= +github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY= -github.com/gagliardetto/solana-go v1.8.4 h1:vmD/JmTlonyXGy39bAo0inMhmbdAwV7rXZtLDMZeodE= -github.com/gagliardetto/solana-go v1.8.4/go.mod h1:i+7aAyNDTHG0jK8GZIBSI4OVvDqkt2Qx+LklYclRNG8= +github.com/gagliardetto/solana-go v1.12.0 h1:rzsbilDPj6p+/DOPXBMLhwMZeBgeRuXjm5zQFCoXgsg= +github.com/gagliardetto/solana-go v1.12.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok= github.com/gagliardetto/utilz v0.1.1 h1:/etW4hl607emKg6R6Lj9jRJ9d6ue2AQOyjhuAwjzs1U= @@ -668,7 +646,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -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= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -704,7 +681,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= @@ -789,7 +765,6 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= 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.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= @@ -823,15 +798,12 @@ github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMN github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= 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.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.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= @@ -904,7 +876,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -1007,10 +978,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= -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= @@ -1021,7 +990,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -1046,7 +1014,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -1096,7 +1063,6 @@ github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/z github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 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.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -1197,7 +1163,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -1218,7 +1183,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -1309,7 +1273,6 @@ github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfW github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= 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= @@ -1322,8 +1285,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -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.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -1336,7 +1297,6 @@ github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57J github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= 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= @@ -1345,7 +1305,6 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ= github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY= -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= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= @@ -1357,7 +1316,6 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -1432,12 +1390,14 @@ github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0 github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix h1:DPJD++yKLSx0EfT+U14P8vLVxjXFmoIETiCO9lVwQo8= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= -github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= -github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.36 h1:KSpO8I+JOiuyN4FuXsV471sPorGF//PAqwq2Cm4gRK0= +github.com/smartcontractkit/chain-selectors v1.0.36/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000 h1:6Zzr/R1j6P7bbvcUlt5WUIbItvrrGdGzIsiAzQezcwo= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241218114855-f74219171000/go.mod h1:ncjd6mPZSRlelEqH/2KeLE1pU3UlqzBSn8RYkEoECzY= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b h1:UBXi9Yj8YSMHDDaxQLu273x1fWjyEL9xP58nuJsqZfg= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250103152858-8973fd0c912b/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550 h1:rRs74zjDJ7do5aYEXSU/sOnLnlbYCNqM8BrvEx/0NH8= github.com/smartcontractkit/chainlink-common v0.4.1-0.20241223143929-db7919d60550/go.mod h1:yti7e1+G9hhkYhj+L5sVUULn9Bn3bBL5/AxaNqdJ5YQ= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= @@ -1480,7 +1440,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= -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/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= @@ -1499,7 +1458,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -1509,15 +1467,13 @@ 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.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75 h1:ZqpS7rAhhKD7S7DnrpEdrnW1/gZcv82ytpMviovkli4= -github.com/streamingfast/logging v0.0.0-20220405224725-2755dab2ce75/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= +github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= 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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1549,9 +1505,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w= -github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= @@ -1560,12 +1513,10 @@ github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrj github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA= 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.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= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -1573,7 +1524,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= @@ -1600,10 +1550,8 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8= github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= @@ -1614,10 +1562,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= 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/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -1625,7 +1569,6 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -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= @@ -1652,7 +1595,6 @@ go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1 go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -1664,12 +1606,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSv go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= -go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= 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.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= 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= @@ -1743,15 +1683,12 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= @@ -1782,7 +1719,6 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= @@ -1797,8 +1733,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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= 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= @@ -1827,15 +1763,14 @@ 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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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= @@ -1886,8 +1821,8 @@ 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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= 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= @@ -1923,7 +1858,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1939,7 +1873,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2015,7 +1948,6 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.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.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -2064,7 +1996,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-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= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -2074,7 +2005,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn 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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -2110,8 +2040,8 @@ 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.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= 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= @@ -2129,7 +2059,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt 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.10.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= @@ -2154,7 +2083,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl 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.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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -2165,7 +2093,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 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= @@ -2215,7 +2142,6 @@ google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE 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.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 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= @@ -2268,16 +2194,13 @@ gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 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.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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=