From 3af83ed01439648354ac6b348d61b0f9594b99ec Mon Sep 17 00:00:00 2001 From: Mateusz Sekara Date: Fri, 24 May 2024 19:37:23 +0200 Subject: [PATCH 01/19] Index only the fifth word to reduce the db size overhead (#13315) Co-authored-by: Domino Valdano --- .changeset/big-bats-grin.md | 5 ++ .../0233_log_poller_word_topic_indexes.sql | 57 ------------------- 2 files changed, 5 insertions(+), 57 deletions(-) create mode 100644 .changeset/big-bats-grin.md diff --git a/.changeset/big-bats-grin.md b/.changeset/big-bats-grin.md new file mode 100644 index 00000000000..a4943e1420b --- /dev/null +++ b/.changeset/big-bats-grin.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +Reducing the scope of 0233 migration to include only 5th word index which is required for CCIP #db_update diff --git a/core/store/migrate/migrations/0233_log_poller_word_topic_indexes.sql b/core/store/migrate/migrations/0233_log_poller_word_topic_indexes.sql index e155e207996..31f222dd7fd 100644 --- a/core/store/migrate/migrations/0233_log_poller_word_topic_indexes.sql +++ b/core/store/migrate/migrations/0233_log_poller_word_topic_indexes.sql @@ -1,65 +1,8 @@ -- +goose Up -drop index if exists evm.evm_logs_idx_data_word_one; -drop index if exists evm.evm_logs_idx_data_word_two; -drop index if exists evm.evm_logs_idx_data_word_three; -drop index if exists evm.evm_logs_idx_data_word_four; -drop index if exists evm.evm_logs_idx_topic_two; -drop index if exists evm.evm_logs_idx_topic_three; -drop index if exists evm.evm_logs_idx_topic_four; - -create index evm_logs_idx_data_word_one - on evm.logs (address, event_sig, evm_chain_id, "substring"(data, 1, 32)); - -create index evm_logs_idx_data_word_two - on evm.logs (address, event_sig, evm_chain_id, "substring"(data, 33, 32)); - -create index evm_logs_idx_data_word_three - on evm.logs (address, event_sig, evm_chain_id, "substring"(data, 65, 32)); - -create index evm_logs_idx_data_word_four - on evm.logs (address, event_sig, evm_chain_id, "substring"(data, 97, 32)); - create index evm_logs_idx_data_word_five on evm.logs (address, event_sig, evm_chain_id, "substring"(data, 129, 32)); -create index evm_logs_idx_topic_two - on evm.logs (address, event_sig, evm_chain_id, (topics[2])); - -create index evm_logs_idx_topic_three - on evm.logs (address, event_sig, evm_chain_id, (topics[3])); - -create index evm_logs_idx_topic_four - on evm.logs (address, event_sig, evm_chain_id, (topics[4])); - -- +goose Down -drop index if exists evm.evm_logs_idx_data_word_one; -drop index if exists evm.evm_logs_idx_data_word_two; -drop index if exists evm.evm_logs_idx_data_word_three; -drop index if exists evm.evm_logs_idx_data_word_four; drop index if exists evm.evm_logs_idx_data_word_five; -drop index if exists evm.evm_logs_idx_topic_two; -drop index if exists evm.evm_logs_idx_topic_three; -drop index if exists evm.evm_logs_idx_topic_four; - -create index evm_logs_idx_data_word_one - on evm.logs ("substring"(data, 1, 32)); - -create index evm_logs_idx_data_word_two - on evm.logs ("substring"(data, 33, 32)); - -create index evm_logs_idx_data_word_three - on evm.logs ("substring"(data, 65, 32)); - -create index evm_logs_idx_data_word_four - on evm.logs ("substring"(data, 97, 32)); - -create index evm_logs_idx_topic_two - on evm.logs ((topics[2])); - -create index evm_logs_idx_topic_three - on evm.logs ((topics[3])); - -create index evm_logs_idx_topic_four - on evm.logs ((topics[4])); \ No newline at end of file From 920ce3109f14160d4068bd0877b30d12ddb19ef8 Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Fri, 24 May 2024 15:57:48 -0700 Subject: [PATCH 02/19] @chainlink.contracts release v1.1.1 (#13039) * @chainlink/contracts - Update changelog for contracts/1.1.1 beta creation * @chainlink/contracts - update 1.1.1 with latest develop * chore: increase solhint max warnings to 2 (#13234) * @chainlink/contracts - Finalizing date for 1.1.1 in changelog --------- Co-authored-by: Erik Burton --- contracts/.changeset/bright-dingos-attend.md | 5 --- contracts/.changeset/calm-maps-vanish.md | 5 --- contracts/.changeset/fresh-rivers-think.md | 5 --- contracts/.changeset/funny-eagles-know.md | 5 --- contracts/.changeset/fuzzy-bags-watch.md | 5 --- contracts/.changeset/heavy-horses-greet.md | 5 --- contracts/.changeset/hot-weeks-agree.md | 5 --- contracts/.changeset/hungry-days-occur.md | 5 --- contracts/.changeset/kind-snakes-invent.md | 5 --- contracts/.changeset/lucky-bugs-buy.md | 5 --- contracts/.changeset/mean-items-talk.md | 5 --- contracts/.changeset/mighty-socks-roll.md | 5 --- contracts/.changeset/new-crews-deny.md | 5 --- contracts/.changeset/new-guests-accept.md | 5 --- contracts/.changeset/old-pianos-trade.md | 5 --- contracts/.changeset/old-seas-doubt.md | 5 --- contracts/.changeset/plenty-ligers-joke.md | 5 --- contracts/.changeset/poor-panthers-build.md | 5 --- contracts/.changeset/proud-tables-grab.md | 5 --- contracts/.changeset/quiet-donkeys-hang.md | 5 --- contracts/.changeset/shiny-dragons-sparkle.md | 5 --- contracts/.changeset/silly-bags-applaud.md | 5 --- contracts/.changeset/small-paws-crash.md | 5 --- contracts/.changeset/stupid-horses-promise.md | 5 --- .../.changeset/tasty-kangaroos-approve.md | 5 --- contracts/.changeset/tender-jokes-do.md | 5 --- contracts/.changeset/three-hotels-agree.md | 5 --- contracts/.changeset/tidy-stingrays-care.md | 5 --- contracts/CHANGELOG.md | 44 ++++++++++++++++++- contracts/package.json | 2 +- 30 files changed, 44 insertions(+), 142 deletions(-) delete mode 100644 contracts/.changeset/bright-dingos-attend.md delete mode 100644 contracts/.changeset/calm-maps-vanish.md delete mode 100644 contracts/.changeset/fresh-rivers-think.md delete mode 100644 contracts/.changeset/funny-eagles-know.md delete mode 100644 contracts/.changeset/fuzzy-bags-watch.md delete mode 100644 contracts/.changeset/heavy-horses-greet.md delete mode 100644 contracts/.changeset/hot-weeks-agree.md delete mode 100644 contracts/.changeset/hungry-days-occur.md delete mode 100644 contracts/.changeset/kind-snakes-invent.md delete mode 100644 contracts/.changeset/lucky-bugs-buy.md delete mode 100644 contracts/.changeset/mean-items-talk.md delete mode 100644 contracts/.changeset/mighty-socks-roll.md delete mode 100644 contracts/.changeset/new-crews-deny.md delete mode 100644 contracts/.changeset/new-guests-accept.md delete mode 100644 contracts/.changeset/old-pianos-trade.md delete mode 100644 contracts/.changeset/old-seas-doubt.md delete mode 100644 contracts/.changeset/plenty-ligers-joke.md delete mode 100644 contracts/.changeset/poor-panthers-build.md delete mode 100644 contracts/.changeset/proud-tables-grab.md delete mode 100644 contracts/.changeset/quiet-donkeys-hang.md delete mode 100644 contracts/.changeset/shiny-dragons-sparkle.md delete mode 100644 contracts/.changeset/silly-bags-applaud.md delete mode 100644 contracts/.changeset/small-paws-crash.md delete mode 100644 contracts/.changeset/stupid-horses-promise.md delete mode 100644 contracts/.changeset/tasty-kangaroos-approve.md delete mode 100644 contracts/.changeset/tender-jokes-do.md delete mode 100644 contracts/.changeset/three-hotels-agree.md delete mode 100644 contracts/.changeset/tidy-stingrays-care.md diff --git a/contracts/.changeset/bright-dingos-attend.md b/contracts/.changeset/bright-dingos-attend.md deleted file mode 100644 index 9d376e40c05..00000000000 --- a/contracts/.changeset/bright-dingos-attend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -#internal diff --git a/contracts/.changeset/calm-maps-vanish.md b/contracts/.changeset/calm-maps-vanish.md deleted file mode 100644 index e9dfcdd53b0..00000000000 --- a/contracts/.changeset/calm-maps-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -withdraw in offchain mode #bugfix diff --git a/contracts/.changeset/fresh-rivers-think.md b/contracts/.changeset/fresh-rivers-think.md deleted file mode 100644 index 44fa349c353..00000000000 --- a/contracts/.changeset/fresh-rivers-think.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -add upkeepCharged event #bugfix diff --git a/contracts/.changeset/funny-eagles-know.md b/contracts/.changeset/funny-eagles-know.md deleted file mode 100644 index 46827824ad8..00000000000 --- a/contracts/.changeset/funny-eagles-know.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -#wip addCapability udpates diff --git a/contracts/.changeset/fuzzy-bags-watch.md b/contracts/.changeset/fuzzy-bags-watch.md deleted file mode 100644 index 31e164df424..00000000000 --- a/contracts/.changeset/fuzzy-bags-watch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -fix solhint issues diff --git a/contracts/.changeset/heavy-horses-greet.md b/contracts/.changeset/heavy-horses-greet.md deleted file mode 100644 index 51912232c26..00000000000 --- a/contracts/.changeset/heavy-horses-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -support decimals #added diff --git a/contracts/.changeset/hot-weeks-agree.md b/contracts/.changeset/hot-weeks-agree.md deleted file mode 100644 index 5bd77e9ebe5..00000000000 --- a/contracts/.changeset/hot-weeks-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -add function to remove node operators from capability registry diff --git a/contracts/.changeset/hungry-days-occur.md b/contracts/.changeset/hungry-days-occur.md deleted file mode 100644 index ca1a703a472..00000000000 --- a/contracts/.changeset/hungry-days-occur.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -set cleanup #bugfix diff --git a/contracts/.changeset/kind-snakes-invent.md b/contracts/.changeset/kind-snakes-invent.md deleted file mode 100644 index abd00ce9c85..00000000000 --- a/contracts/.changeset/kind-snakes-invent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -add getters #internal diff --git a/contracts/.changeset/lucky-bugs-buy.md b/contracts/.changeset/lucky-bugs-buy.md deleted file mode 100644 index 98a17d74af9..00000000000 --- a/contracts/.changeset/lucky-bugs-buy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -Add function to update node operator' diff --git a/contracts/.changeset/mean-items-talk.md b/contracts/.changeset/mean-items-talk.md deleted file mode 100644 index e03d49335ad..00000000000 --- a/contracts/.changeset/mean-items-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -#wip Keystone custom error diff --git a/contracts/.changeset/mighty-socks-roll.md b/contracts/.changeset/mighty-socks-roll.md deleted file mode 100644 index 8364158b5a2..00000000000 --- a/contracts/.changeset/mighty-socks-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -bump versions and fix solhint diff --git a/contracts/.changeset/new-crews-deny.md b/contracts/.changeset/new-crews-deny.md deleted file mode 100644 index 170cc724344..00000000000 --- a/contracts/.changeset/new-crews-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -vrfv2plus - account for num words in coordinator gas overhead in v2plus wrapper diff --git a/contracts/.changeset/new-guests-accept.md b/contracts/.changeset/new-guests-accept.md deleted file mode 100644 index 080fe17b2bc..00000000000 --- a/contracts/.changeset/new-guests-accept.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -minor fixes #bugfix diff --git a/contracts/.changeset/old-pianos-trade.md b/contracts/.changeset/old-pianos-trade.md deleted file mode 100644 index 10c0097bded..00000000000 --- a/contracts/.changeset/old-pianos-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -#wip Add Capability Registry skeleton diff --git a/contracts/.changeset/old-seas-doubt.md b/contracts/.changeset/old-seas-doubt.md deleted file mode 100644 index d559b3f5641..00000000000 --- a/contracts/.changeset/old-seas-doubt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -#internal Keystone - rename type to id diff --git a/contracts/.changeset/plenty-ligers-joke.md b/contracts/.changeset/plenty-ligers-joke.md deleted file mode 100644 index 9d376e40c05..00000000000 --- a/contracts/.changeset/plenty-ligers-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -#internal diff --git a/contracts/.changeset/poor-panthers-build.md b/contracts/.changeset/poor-panthers-build.md deleted file mode 100644 index 2c21cae5d7a..00000000000 --- a/contracts/.changeset/poor-panthers-build.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -Offchain settlement fix #bugfix diff --git a/contracts/.changeset/proud-tables-grab.md b/contracts/.changeset/proud-tables-grab.md deleted file mode 100644 index f2799f86ee9..00000000000 --- a/contracts/.changeset/proud-tables-grab.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -get available erc20s for payment #bugfix diff --git a/contracts/.changeset/quiet-donkeys-hang.md b/contracts/.changeset/quiet-donkeys-hang.md deleted file mode 100644 index 9d376e40c05..00000000000 --- a/contracts/.changeset/quiet-donkeys-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -#internal diff --git a/contracts/.changeset/shiny-dragons-sparkle.md b/contracts/.changeset/shiny-dragons-sparkle.md deleted file mode 100644 index 7657b5f1446..00000000000 --- a/contracts/.changeset/shiny-dragons-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -use safe lib for approve #bugfix diff --git a/contracts/.changeset/silly-bags-applaud.md b/contracts/.changeset/silly-bags-applaud.md deleted file mode 100644 index e2ec0c2e408..00000000000 --- a/contracts/.changeset/silly-bags-applaud.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -implement add don for capability regsitry diff --git a/contracts/.changeset/small-paws-crash.md b/contracts/.changeset/small-paws-crash.md deleted file mode 100644 index 3a3efa36805..00000000000 --- a/contracts/.changeset/small-paws-crash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -Update operatorforwarder tests and pull out of dev/ diff --git a/contracts/.changeset/stupid-horses-promise.md b/contracts/.changeset/stupid-horses-promise.md deleted file mode 100644 index b7de55939d8..00000000000 --- a/contracts/.changeset/stupid-horses-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -implement remove nodes on capability registry diff --git a/contracts/.changeset/tasty-kangaroos-approve.md b/contracts/.changeset/tasty-kangaroos-approve.md deleted file mode 100644 index 006b3143bb6..00000000000 --- a/contracts/.changeset/tasty-kangaroos-approve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -contracts work diff --git a/contracts/.changeset/tender-jokes-do.md b/contracts/.changeset/tender-jokes-do.md deleted file mode 100644 index 5500fe0ca00..00000000000 --- a/contracts/.changeset/tender-jokes-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -Implement function to add node operators to the capability registry' diff --git a/contracts/.changeset/three-hotels-agree.md b/contracts/.changeset/three-hotels-agree.md deleted file mode 100644 index 66ed4b2377e..00000000000 --- a/contracts/.changeset/three-hotels-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -Add function to update nodes in capability registry diff --git a/contracts/.changeset/tidy-stingrays-care.md b/contracts/.changeset/tidy-stingrays-care.md deleted file mode 100644 index 9d376e40c05..00000000000 --- a/contracts/.changeset/tidy-stingrays-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@chainlink/contracts": patch ---- - -#internal diff --git a/contracts/CHANGELOG.md b/contracts/CHANGELOG.md index 3139312e325..ba6dabfeb05 100644 --- a/contracts/CHANGELOG.md +++ b/contracts/CHANGELOG.md @@ -1,5 +1,48 @@ # @chainlink/contracts +## 1.1.1 - 2024-05-23 + +### Patch Changes + +- [#13031](https://github.com/smartcontractkit/chainlink/pull/13031) [`04b42f1dd7`](https://github.com/smartcontractkit/chainlink/commit/04b42f1dd7db449e5267e2491a9ba6971f41b1bf) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal + +- [#13058](https://github.com/smartcontractkit/chainlink/pull/13058) [`a34a17ae9d`](https://github.com/smartcontractkit/chainlink/commit/a34a17ae9d62679a1ff15a7703f5cbcf6dfd1d0f) Thanks [@shileiwill](https://github.com/shileiwill)! - withdraw in offchain mode #bugfix + +- [#13096](https://github.com/smartcontractkit/chainlink/pull/13096) [`2c08c8c1a5`](https://github.com/smartcontractkit/chainlink/commit/2c08c8c1a58ea4b7c09b0d5a5ca3b8a677beb9f4) Thanks [@shileiwill](https://github.com/shileiwill)! - add upkeepCharged event #bugfix + +- [#13185](https://github.com/smartcontractkit/chainlink/pull/13185) [`286e6af423`](https://github.com/smartcontractkit/chainlink/commit/286e6af42357dc08acc23659ac6d486dff458478) Thanks [@RensR](https://github.com/RensR)! - fix solhint issues + +- [#13129](https://github.com/smartcontractkit/chainlink/pull/13129) [`811fe603ae`](https://github.com/smartcontractkit/chainlink/commit/811fe603ae67ec13b7f6ca1fb034840d33ad5303) Thanks [@RensR](https://github.com/RensR)! - bump versions and fix solhint + +- [#13008](https://github.com/smartcontractkit/chainlink/pull/13008) [`841fe61daa`](https://github.com/smartcontractkit/chainlink/commit/841fe61daa90b980f1e1622d2f7bd8f850b55462) Thanks [@HenryNguyen5](https://github.com/HenryNguyen5)! - #internal Keystone - rename type to id + +- [#13036](https://github.com/smartcontractkit/chainlink/pull/13036) [`77efb61e56`](https://github.com/smartcontractkit/chainlink/commit/77efb61e562fb456ac5bef3b6a528904cda000ec) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal + +- [#13088](https://github.com/smartcontractkit/chainlink/pull/13088) [`29b16360fb`](https://github.com/smartcontractkit/chainlink/commit/29b16360fb41e4372f72fe744aaf3ee8234a9b67) Thanks [@shileiwill](https://github.com/shileiwill)! - get available erc20s for payment #bugfix + +- [#12962](https://github.com/smartcontractkit/chainlink/pull/12962) [`62d31d0a6e`](https://github.com/smartcontractkit/chainlink/commit/62d31d0a6ea724e7ac00d5860d60cc93d88a3f35) Thanks [@archseer](https://github.com/archseer)! - #internal + +- [#13103](https://github.com/smartcontractkit/chainlink/pull/13103) [`54f7c9c8f5`](https://github.com/smartcontractkit/chainlink/commit/54f7c9c8f5508d0d0a063eb435404b4164723300) Thanks [@DeividasK](https://github.com/DeividasK)! - implement add don for capability regsitry + +- [#12983](https://github.com/smartcontractkit/chainlink/pull/12983) [`644f5f271d`](https://github.com/smartcontractkit/chainlink/commit/644f5f271d9ed47e999e1d9aa4b99e5de0fd8b89) Thanks [@austinborn](https://github.com/austinborn)! - Update operatorforwarder tests and pull out of dev/ + +- [#13102](https://github.com/smartcontractkit/chainlink/pull/13102) [`700a827194`](https://github.com/smartcontractkit/chainlink/commit/700a82719451611381ab5dbb94fe00547660440b) Thanks [@cds95](https://github.com/cds95)! - implement remove nodes on capability registry + +- [#13080](https://github.com/smartcontractkit/chainlink/pull/13080) [`36cc95f625`](https://github.com/smartcontractkit/chainlink/commit/36cc95f6256b5ba418a916de2c9dc9597508147a) Thanks [@cds95](https://github.com/cds95)! - Add function to update nodes in capability registry + +- [#13022](https://github.com/smartcontractkit/chainlink/pull/13022) [`2805fa6c9b`](https://github.com/smartcontractkit/chainlink/commit/2805fa6c9b469d535edcd3d66c08e1d22bbaa2d0) Thanks [@cds95](https://github.com/cds95)! - #internal + + +- [#12812](https://github.com/smartcontractkit/chainlink/pull/12812) [`5b33a3296f`](https://github.com/smartcontractkit/chainlink/commit/5b33a3296f895cec8a23ba2e235989868f398ddb) Thanks [@shileiwill](https://github.com/shileiwill)! - Support decimals #added + +- [#12979](https://github.com/smartcontractkit/chainlink/pull/12979) [`0c4c24ad8c`](https://github.com/smartcontractkit/chainlink/commit/0c4c24ad8c95e505cd2a29be711cc40e612658b0) Thanks [@cds95](https://github.com/cds95)! - Add function to remove node operators from capability registry + +- [#12991](https://github.com/smartcontractkit/chainlink/pull/12991) [`929312681f`](https://github.com/smartcontractkit/chainlink/commit/929312681fb27529915912e8bd6e4000559ea77f) Thanks [@cds95](https://github.com/cds95)! - Add function to update node operator' + +- [#12669](https://github.com/smartcontractkit/chainlink/pull/12669) [`3134ce8868`](https://github.com/smartcontractkit/chainlink/commit/3134ce8868ccc22bd4ae670c8b0bfda5fa78a332) Thanks [@leeyikjiun](https://github.com/leeyikjiun)! - vrfv2plus - account for num words in coordinator gas overhead in v2plus wrapper + +- [#12906](https://github.com/smartcontractkit/chainlink/pull/12906) [`365c38be8b`](https://github.com/smartcontractkit/chainlink/commit/365c38be8b589d5ffa0b21755dcb40e2e4205652) Thanks [@cds95](https://github.com/cds95)! - Implement function to add node operators to the capability registry' + ## 1.1.0 - 2024-04-23 ### Minor Changes @@ -81,7 +124,6 @@ - [#12531](https://github.com/smartcontractkit/chainlink/pull/12531) [`88e010d604`](https://github.com/smartcontractkit/chainlink/commit/88e010d604682c54c4f99e0a0916f94c0d13ece6) Thanks [@jinhoonbang](https://github.com/jinhoonbang)! - increase num optimizations to 500 for v2.5 coordinator - ## 1.0.0 - 2024-03-25 - Moved `VRFCoordinatorV2Mock.sol` to src/v0.8/vrf/mocks diff --git a/contracts/package.json b/contracts/package.json index 0101a202d4f..9b98aa9b05c 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/contracts", - "version": "1.1.0", + "version": "1.1.1", "description": "Chainlink smart contracts", "author": "Chainlink devs", "license": "MIT", From 4fce66616dd9b63e5a60bd6b62900b5c12c57995 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 27 May 2024 11:43:18 +0200 Subject: [PATCH 03/19] bump typescript-eslint and minor npm dependencies (#13270) * bump typescript-eslint * bump patches * bump eslint again * bump changesets --- contracts/package.json | 16 +- contracts/pnpm-lock.yaml | 491 ++++++++++++++++++--------------------- 2 files changed, 236 insertions(+), 271 deletions(-) diff --git a/contracts/package.json b/contracts/package.json index 9b98aa9b05c..775ee529a4d 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -38,9 +38,9 @@ "@ethersproject/contracts": "~5.7.0", "@ethersproject/providers": "~5.7.2", "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", - "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-ethers": "^3.0.6", "@nomicfoundation/hardhat-network-helpers": "^1.0.9", - "@nomicfoundation/hardhat-verify": "^2.0.6", + "@nomicfoundation/hardhat-verify": "^2.0.7", "@typechain/ethers-v5": "^7.2.0", "@typechain/hardhat": "^7.0.0", "@types/cbor": "~5.0.1", @@ -48,15 +48,15 @@ "@types/debug": "^4.1.12", "@types/deep-equal-in-any-order": "^1.0.3", "@types/mocha": "^10.0.6", - "@types/node": "^20.12.11", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@types/node": "^20.12.12", + "@typescript-eslint/eslint-plugin": "^7.10.0", + "@typescript-eslint/parser": "^7.10.0", "abi-to-sol": "^0.6.6", "cbor": "^5.2.0", "chai": "^4.3.10", "debug": "^4.3.4", "deep-equal-in-any-order": "^2.0.6", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "ethers": "~5.7.2", @@ -74,8 +74,8 @@ "typescript": "^5.4.5" }, "dependencies": { - "@changesets/changelog-github": "^0.4.8", - "@changesets/cli": "~2.26.2", + "@changesets/changelog-github": "^0.5.0", + "@changesets/cli": "~2.27.3", "@eth-optimism/contracts": "0.6.0", "@openzeppelin/contracts": "4.9.3", "@openzeppelin/contracts-upgradeable": "4.9.3", diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index 51a4ad6a4ca..ee36e0a0c55 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -9,11 +9,11 @@ overrides: dependencies: '@changesets/changelog-github': - specifier: ^0.4.8 - version: 0.4.8 + specifier: ^0.5.0 + version: 0.5.0 '@changesets/cli': - specifier: ~2.26.2 - version: 2.26.2 + specifier: ~2.27.3 + version: 2.27.3 '@eth-optimism/contracts': specifier: 0.6.0 version: 0.6.0(ethers@5.7.2) @@ -50,14 +50,14 @@ devDependencies: specifier: ^1.0.6 version: 1.0.6(@nomiclabs/hardhat-ethers@2.2.3)(chai@4.4.1)(ethers@5.7.2)(hardhat@2.20.1) '@nomicfoundation/hardhat-ethers': - specifier: ^3.0.5 - version: 3.0.5(ethers@5.7.2)(hardhat@2.20.1) + specifier: ^3.0.6 + version: 3.0.6(ethers@5.7.2)(hardhat@2.20.1) '@nomicfoundation/hardhat-network-helpers': specifier: ^1.0.9 version: 1.0.10(hardhat@2.20.1) '@nomicfoundation/hardhat-verify': - specifier: ^2.0.6 - version: 2.0.6(hardhat@2.20.1) + specifier: ^2.0.7 + version: 2.0.7(hardhat@2.20.1) '@typechain/ethers-v5': specifier: ^7.2.0 version: 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.4.5) @@ -80,14 +80,14 @@ devDependencies: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^20.12.11 - version: 20.12.11 + specifier: ^20.12.12 + version: 20.12.12 '@typescript-eslint/eslint-plugin': - specifier: ^6.21.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.5) + specifier: ^7.10.0 + version: 7.10.0(@typescript-eslint/parser@7.10.0)(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/parser': - specifier: ^6.21.0 - version: 6.21.0(eslint@8.57.0)(typescript@5.4.5) + specifier: ^7.10.0 + version: 7.10.0(eslint@8.57.0)(typescript@5.4.5) abi-to-sol: specifier: ^0.6.6 version: 0.6.6 @@ -104,7 +104,7 @@ devDependencies: specifier: ^2.0.6 version: 2.0.6 eslint: - specifier: ^8.56.0 + specifier: ^8.57.0 version: 8.57.0 eslint-config-prettier: specifier: ^9.1.0 @@ -144,7 +144,7 @@ devDependencies: version: 0.1.0(prettier-plugin-solidity@1.3.1)(prettier@3.2.5) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.12.11)(typescript@5.4.5) + version: 10.9.2(@types/node@20.12.12)(typescript@5.4.5) typechain: specifier: ^8.2.1 version: 8.3.2(typescript@5.4.5) @@ -183,14 +183,14 @@ packages: dependencies: regenerator-runtime: 0.14.1 - /@changesets/apply-release-plan@6.1.4: - resolution: {integrity: sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew==} + /@changesets/apply-release-plan@7.0.1: + resolution: {integrity: sha512-aPdSq/R++HOyfEeBGjEe6LNG8gs0KMSyRETD/J2092OkNq8mOioAxyKjMbvVUdzgr/HTawzMOz7lfw339KnsCA==} dependencies: '@babel/runtime': 7.24.0 - '@changesets/config': 2.3.1 - '@changesets/get-version-range-type': 0.3.2 - '@changesets/git': 2.0.0 - '@changesets/types': 5.2.1 + '@changesets/config': 3.0.0 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.0 + '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 detect-indent: 6.1.0 fs-extra: 7.0.1 @@ -201,61 +201,60 @@ packages: semver: 7.6.2 dev: false - /@changesets/assemble-release-plan@5.2.4: - resolution: {integrity: sha512-xJkWX+1/CUaOUWTguXEbCDTyWJFECEhmdtbkjhn5GVBGxdP/JwaHBIU9sW3FR6gD07UwZ7ovpiPclQZs+j+mvg==} + /@changesets/assemble-release-plan@6.0.0: + resolution: {integrity: sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw==} dependencies: '@babel/runtime': 7.24.0 - '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.6 - '@changesets/types': 5.2.1 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.0.0 + '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 semver: 7.6.2 dev: false - /@changesets/changelog-git@0.1.14: - resolution: {integrity: sha512-+vRfnKtXVWsDDxGctOfzJsPhaCdXRYoe+KyWYoq5X/GqoISREiat0l3L8B0a453B2B4dfHGcZaGyowHbp9BSaA==} + /@changesets/changelog-git@0.2.0: + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} dependencies: - '@changesets/types': 5.2.1 + '@changesets/types': 6.0.0 dev: false - /@changesets/changelog-github@0.4.8: - resolution: {integrity: sha512-jR1DHibkMAb5v/8ym77E4AMNWZKB5NPzw5a5Wtqm1JepAuIF+hrKp2u04NKM14oBZhHglkCfrla9uq8ORnK/dw==} + /@changesets/changelog-github@0.5.0: + resolution: {integrity: sha512-zoeq2LJJVcPJcIotHRJEEA2qCqX0AQIeFE+L21L8sRLPVqDhSXY8ZWAt2sohtBpFZkBwu+LUwMSKRr2lMy3LJA==} dependencies: - '@changesets/get-github-info': 0.5.2 - '@changesets/types': 5.2.1 + '@changesets/get-github-info': 0.6.0 + '@changesets/types': 6.0.0 dotenv: 8.6.0 transitivePeerDependencies: - encoding dev: false - /@changesets/cli@2.26.2: - resolution: {integrity: sha512-dnWrJTmRR8bCHikJHl9b9HW3gXACCehz4OasrXpMp7sx97ECuBGGNjJhjPhdZNCvMy9mn4BWdplI323IbqsRig==} + /@changesets/cli@2.27.3: + resolution: {integrity: sha512-ve/VpWApILlSs8cr0okNx5C2LKRawI9XZgvfmf58S8sar2nhx5DPJREFXYZBahs0FeTfvH0rdVl+nGe8QF45Ig==} hasBin: true dependencies: '@babel/runtime': 7.24.0 - '@changesets/apply-release-plan': 6.1.4 - '@changesets/assemble-release-plan': 5.2.4 - '@changesets/changelog-git': 0.1.14 - '@changesets/config': 2.3.1 - '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.6 - '@changesets/get-release-plan': 3.0.17 - '@changesets/git': 2.0.0 - '@changesets/logger': 0.0.5 - '@changesets/pre': 1.0.14 - '@changesets/read': 0.5.9 - '@changesets/types': 5.2.1 - '@changesets/write': 0.2.3 + '@changesets/apply-release-plan': 7.0.1 + '@changesets/assemble-release-plan': 6.0.0 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.0 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.0.0 + '@changesets/get-release-plan': 4.0.0 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.1 '@manypkg/get-packages': 1.1.3 - '@types/is-ci': 3.0.4 '@types/semver': 7.5.0 ansi-colors: 4.1.3 chalk: 2.4.2 + ci-info: 3.9.0 enquirer: 2.3.6 external-editor: 3.1.0 fs-extra: 7.0.1 human-id: 1.0.2 - is-ci: 3.0.1 meow: 6.1.1 outdent: 0.5.0 p-limit: 2.3.0 @@ -267,36 +266,36 @@ packages: tty-table: 4.2.3 dev: false - /@changesets/config@2.3.1: - resolution: {integrity: sha512-PQXaJl82CfIXddUOppj4zWu+987GCw2M+eQcOepxN5s+kvnsZOwjEJO3DH9eVy+OP6Pg/KFEWdsECFEYTtbg6w==} + /@changesets/config@3.0.0: + resolution: {integrity: sha512-o/rwLNnAo/+j9Yvw9mkBQOZySDYyOr/q+wptRLcAVGlU6djOeP9v1nlalbL9MFsobuBVQbZCTp+dIzdq+CLQUA==} dependencies: - '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.6 - '@changesets/logger': 0.0.5 - '@changesets/types': 5.2.1 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.0.0 + '@changesets/logger': 0.1.0 + '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 micromatch: 4.0.5 dev: false - /@changesets/errors@0.1.4: - resolution: {integrity: sha512-HAcqPF7snsUJ/QzkWoKfRfXushHTu+K5KZLJWPb34s4eCZShIf8BFO3fwq6KU8+G7L5KdtN2BzQAXOSXEyiY9Q==} + /@changesets/errors@0.2.0: + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} dependencies: extendable-error: 0.1.7 dev: false - /@changesets/get-dependents-graph@1.3.6: - resolution: {integrity: sha512-Q/sLgBANmkvUm09GgRsAvEtY3p1/5OCzgBE5vX3vgb5CvW0j7CEljocx5oPXeQSNph6FXulJlXV3Re/v3K3P3Q==} + /@changesets/get-dependents-graph@2.0.0: + resolution: {integrity: sha512-cafUXponivK4vBgZ3yLu944mTvam06XEn2IZGjjKc0antpenkYANXiiE6GExV/yKdsCnE8dXVZ25yGqLYZmScA==} dependencies: - '@changesets/types': 5.2.1 + '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 chalk: 2.4.2 fs-extra: 7.0.1 semver: 7.6.2 dev: false - /@changesets/get-github-info@0.5.2: - resolution: {integrity: sha512-JppheLu7S114aEs157fOZDjFqUDpm7eHdq5E8SSR0gUBTEK0cNSHsrSR5a66xs0z3RWuo46QvA3vawp8BxDHvg==} + /@changesets/get-github-info@0.6.0: + resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==} dependencies: dataloader: 1.4.0 node-fetch: 2.6.7 @@ -304,65 +303,65 @@ packages: - encoding dev: false - /@changesets/get-release-plan@3.0.17: - resolution: {integrity: sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==} + /@changesets/get-release-plan@4.0.0: + resolution: {integrity: sha512-9L9xCUeD/Tb6L/oKmpm8nyzsOzhdNBBbt/ZNcjynbHC07WW4E1eX8NMGC5g5SbM5z/V+MOrYsJ4lRW41GCbg3w==} dependencies: '@babel/runtime': 7.24.0 - '@changesets/assemble-release-plan': 5.2.4 - '@changesets/config': 2.3.1 - '@changesets/pre': 1.0.14 - '@changesets/read': 0.5.9 - '@changesets/types': 5.2.1 + '@changesets/assemble-release-plan': 6.0.0 + '@changesets/config': 3.0.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 dev: false - /@changesets/get-version-range-type@0.3.2: - resolution: {integrity: sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg==} + /@changesets/get-version-range-type@0.4.0: + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} dev: false - /@changesets/git@2.0.0: - resolution: {integrity: sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==} + /@changesets/git@3.0.0: + resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} dependencies: '@babel/runtime': 7.24.0 - '@changesets/errors': 0.1.4 - '@changesets/types': 5.2.1 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 is-subdir: 1.2.0 micromatch: 4.0.5 spawndamnit: 2.0.0 dev: false - /@changesets/logger@0.0.5: - resolution: {integrity: sha512-gJyZHomu8nASHpaANzc6bkQMO9gU/ib20lqew1rVx753FOxffnCrJlGIeQVxNWCqM+o6OOleCo/ivL8UAO5iFw==} + /@changesets/logger@0.1.0: + resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} dependencies: chalk: 2.4.2 dev: false - /@changesets/parse@0.3.16: - resolution: {integrity: sha512-127JKNd167ayAuBjUggZBkmDS5fIKsthnr9jr6bdnuUljroiERW7FBTDNnNVyJ4l69PzR57pk6mXQdtJyBCJKg==} + /@changesets/parse@0.4.0: + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} dependencies: - '@changesets/types': 5.2.1 + '@changesets/types': 6.0.0 js-yaml: 3.14.1 dev: false - /@changesets/pre@1.0.14: - resolution: {integrity: sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==} + /@changesets/pre@2.0.0: + resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} dependencies: '@babel/runtime': 7.24.0 - '@changesets/errors': 0.1.4 - '@changesets/types': 5.2.1 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 dev: false - /@changesets/read@0.5.9: - resolution: {integrity: sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==} + /@changesets/read@0.6.0: + resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} dependencies: '@babel/runtime': 7.24.0 - '@changesets/git': 2.0.0 - '@changesets/logger': 0.0.5 - '@changesets/parse': 0.3.16 - '@changesets/types': 5.2.1 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 chalk: 2.4.2 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -372,15 +371,15 @@ packages: resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} dev: false - /@changesets/types@5.2.1: - resolution: {integrity: sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg==} + /@changesets/types@6.0.0: + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} dev: false - /@changesets/write@0.2.3: - resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==} + /@changesets/write@0.3.1: + resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} dependencies: '@babel/runtime': 7.24.0 - '@changesets/types': 5.2.1 + '@changesets/types': 6.0.0 fs-extra: 7.0.1 human-id: 1.0.2 prettier: 2.8.8 @@ -403,8 +402,8 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@eslint-community/regexpp@4.9.1: - resolution: {integrity: sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==} + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true @@ -415,8 +414,8 @@ packages: ajv: 6.12.6 debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 - globals: 13.20.0 - ignore: 5.2.4 + globals: 13.24.0 + ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -773,7 +772,7 @@ packages: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 2.0.2 + '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: @@ -785,8 +784,8 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@2.0.2: - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + /@humanwhocodes/object-schema@2.0.3: + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} dev: true /@jridgewell/resolve-uri@3.1.1: @@ -1056,8 +1055,8 @@ packages: ordinal: 1.0.3 dev: true - /@nomicfoundation/hardhat-ethers@3.0.5(ethers@5.7.2)(hardhat@2.20.1): - resolution: {integrity: sha512-RNFe8OtbZK6Ila9kIlHp0+S80/0Bu/3p41HUpaRIoHLm6X3WekTd83vob3rE54Duufu1edCiBDxspBzi2rxHHw==} + /@nomicfoundation/hardhat-ethers@3.0.6(ethers@5.7.2)(hardhat@2.20.1): + resolution: {integrity: sha512-/xzkFQAaHQhmIAYOQmvHBPwL+NkwLzT9gRZBsgWUYeV+E6pzXsBQsHfRYbAZ3XEYare+T7S+5Tg/1KDJgepSkA==} peerDependencies: ethers: ^6.1.0 hardhat: ^2.0.0 @@ -1079,8 +1078,8 @@ packages: hardhat: 2.20.1(ts-node@10.9.2)(typescript@5.4.5) dev: true - /@nomicfoundation/hardhat-verify@2.0.6(hardhat@2.20.1): - resolution: {integrity: sha512-oKUI5fl8QC8jysE2LUBHE6rObzEmccJcc4b43Ov7LFMlCBZJE27qoqGIsg/++wX7L8Jdga+bkejPxl8NvsecpQ==} + /@nomicfoundation/hardhat-verify@2.0.7(hardhat@2.20.1): + resolution: {integrity: sha512-jiYHBX+K6bBN0YhwFHQ5SWWc3dQZliM3pdgpH33C7tnsVACsX1ubZn6gZ9hfwlzG0tyjFM72XQhpaXQ56cE6Ew==} peerDependencies: hardhat: ^2.0.4 dependencies: @@ -1444,13 +1443,13 @@ packages: /@types/bn.js@4.11.6: resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} dependencies: - '@types/node': 20.12.11 + '@types/node': 20.12.12 dev: true /@types/bn.js@5.1.1: resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} dependencies: - '@types/node': 20.12.11 + '@types/node': 20.12.12 dev: true /@types/cacheable-request@6.0.2: @@ -1458,14 +1457,14 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 20.12.11 + '@types/node': 20.12.12 '@types/responselike': 1.0.0 dev: true /@types/cbor@5.0.1: resolution: {integrity: sha512-zVqJy2KzusZPLOgyGJDnOIbu3DxIGGqxYbEwtEEe4Z+la8jwIhOyb+GMrlHafs5tvKruwf8f8qOYP6zTvse/pw==} dependencies: - '@types/node': 20.12.11 + '@types/node': 20.12.12 dev: true /@types/chai-as-promised@7.1.8: @@ -1492,20 +1491,10 @@ packages: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: true - /@types/is-ci@3.0.4: - resolution: {integrity: sha512-AkCYCmwlXeuH89DagDCzvCAyltI2v9lh3U3DqSg/GrBYoReAaWwxfXCqMx9UV5MajLZ4ZFwZzV4cABGIxk2XRw==} - dependencies: - ci-info: 3.9.0 - dev: false - - /@types/json-schema@7.0.13: - resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} - dev: true - /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.12.11 + '@types/node': 20.12.12 dev: true /@types/lru-cache@5.1.1: @@ -1528,8 +1517,8 @@ packages: resolution: {integrity: sha512-7xHmXm/QJ7cbK2laF+YYD7gb5MggHIIQwqyjin3bpEGiSuvScMQ5JZZXPvRipi1MwckTQbJZROMns/JxdnIL1Q==} dev: false - /@types/node@20.12.11: - resolution: {integrity: sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==} + /@types/node@20.12.12: + resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} dependencies: undici-types: 5.26.5 dev: true @@ -1541,7 +1530,7 @@ packages: /@types/pbkdf2@3.1.0: resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} dependencies: - '@types/node': 20.12.11 + '@types/node': 20.12.12 dev: true /@types/prettier@2.7.1: @@ -1551,68 +1540,67 @@ packages: /@types/readable-stream@2.3.15: resolution: {integrity: sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==} dependencies: - '@types/node': 20.12.11 + '@types/node': 20.12.12 safe-buffer: 5.1.2 dev: true /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 20.12.11 + '@types/node': 20.12.12 dev: true /@types/secp256k1@4.0.3: resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} dependencies: - '@types/node': 20.12.11 + '@types/node': 20.12.12 dev: true /@types/semver@7.5.0: resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} + dev: false - /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.0)(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.9.1 - '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.4(supports-color@8.1.1) + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 7.10.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 7.10.0 + '@typescript-eslint/type-utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.10.0 eslint: 8.57.0 graphemer: 1.4.0 - ignore: 5.2.4 + ignore: 5.3.1 natural-compare: 1.4.0 - semver: 7.6.2 - ts-api-utils: 1.0.3(typescript@5.4.5) + ts-api-utils: 1.3.0(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/scope-manager': 7.10.0 + '@typescript-eslint/types': 7.10.0 + '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.10.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 typescript: 5.4.5 @@ -1620,85 +1608,82 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@6.21.0: - resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/scope-manager@7.10.0: + resolution: {integrity: sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==} + engines: {node: ^18.18.0 || >=20.0.0} dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/types': 7.10.0 + '@typescript-eslint/visitor-keys': 7.10.0 dev: true - /@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/type-utils@7.10.0(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) - '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) + '@typescript-eslint/utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5) debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 - ts-api-utils: 1.0.3(typescript@5.4.5) + ts-api-utils: 1.3.0(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.21.0: - resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/types@7.10.0: + resolution: {integrity: sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==} + engines: {node: ^18.18.0 || >=20.0.0} dev: true - /@typescript-eslint/typescript-estree@6.21.0(typescript@5.4.5): - resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/typescript-estree@7.10.0(typescript@5.4.5): + resolution: {integrity: sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/types': 7.10.0 + '@typescript-eslint/visitor-keys': 7.10.0 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.3 + minimatch: 9.0.4 semver: 7.6.2 - ts-api-utils: 1.0.3(typescript@5.4.5) + ts-api-utils: 1.3.0(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/utils@7.10.0(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.13 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) + '@typescript-eslint/scope-manager': 7.10.0 + '@typescript-eslint/types': 7.10.0 + '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) eslint: 8.57.0 - semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@6.21.0: - resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/visitor-keys@7.10.0: + resolution: {integrity: sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==} + engines: {node: ^18.18.0 || >=20.0.0} dependencies: - '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/types': 7.10.0 eslint-visitor-keys: 3.4.3 dev: true @@ -1724,12 +1709,12 @@ packages: - supports-color dev: true - /acorn-jsx@5.3.2(acorn@8.10.0): + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.10.0 + acorn: 8.11.3 dev: true /acorn-walk@8.2.0: @@ -1743,6 +1728,12 @@ packages: hasBin: true dev: true + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /adm-zip@0.4.16: resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==} engines: {node: '>=0.3.0'} @@ -2097,13 +2088,6 @@ packages: responselike: 2.0.1 dev: true - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.1.3 - dev: false - /call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} dependencies: @@ -2544,14 +2528,6 @@ packages: has-property-descriptors: 1.0.0 dev: false - /define-properties@1.1.4: - resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} - engines: {node: '>= 0.4'} - dependencies: - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - dev: false - /define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -2783,7 +2759,7 @@ packages: hasBin: true dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.9.1 + '@eslint-community/regexpp': 4.10.0 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.0 '@humanwhocodes/config-array': 0.11.14 @@ -2805,9 +2781,9 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.20.0 + globals: 13.24.0 graphemer: 1.4.0 - ignore: 5.2.4 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -2828,8 +2804,8 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) eslint-visitor-keys: 3.4.3 dev: true @@ -3043,7 +3019,7 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flat-cache: 3.0.4 + flat-cache: 3.2.0 dev: true /fill-range@7.0.1: @@ -3088,11 +3064,12 @@ packages: pkg-dir: 4.2.0 dev: false - /flat-cache@3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flatted: 3.2.7 + flatted: 3.3.1 + keyv: 4.5.4 rimraf: 3.0.2 dev: true @@ -3101,8 +3078,8 @@ packages: hasBin: true dev: true - /flatted@3.2.7: - resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true /follow-redirects@1.15.6(debug@4.3.4): @@ -3180,10 +3157,6 @@ packages: dev: true optional: true - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: false - /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} dev: false @@ -3209,14 +3182,6 @@ packages: /get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - /get-intrinsic@1.1.3: - resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==} - dependencies: - function-bind: 1.1.1 - has: 1.0.3 - has-symbols: 1.0.3 - dev: false - /get-intrinsic@1.2.2: resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} dependencies: @@ -3242,8 +3207,8 @@ packages: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.1.3 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 dev: false /glob-parent@5.1.2: @@ -3303,8 +3268,8 @@ packages: once: 1.4.0 dev: true - /globals@13.20.0: - resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -3445,7 +3410,7 @@ packages: solc: 0.7.3(debug@4.3.4) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 - ts-node: 10.9.2(@types/node@20.12.11)(typescript@5.4.5) + ts-node: 10.9.2(@types/node@20.12.12)(typescript@5.4.5) tsort: 0.0.1 typescript: 5.4.5 undici: 5.28.4 @@ -3473,7 +3438,7 @@ packages: /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} dependencies: - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.2 dev: false /has-proto@1.0.1: @@ -3497,7 +3462,7 @@ packages: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 dev: false /hash-base@3.1.0: @@ -3592,6 +3557,11 @@ packages: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true + /immutable@4.1.0: resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==} dev: true @@ -3679,13 +3649,6 @@ packages: engines: {node: '>= 0.4'} dev: false - /is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - dependencies: - ci-info: 3.9.0 - dev: false - /is-core-module@2.10.0: resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} dependencies: @@ -3757,14 +3720,14 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-tostringtag: 1.0.0 dev: false /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 dev: false /is-string@1.0.7: @@ -3809,7 +3772,7 @@ packages: /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 dev: false /is-windows@1.0.2: @@ -3916,6 +3879,12 @@ packages: json-buffer: 3.0.1 dev: true + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -4164,8 +4133,8 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + /minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 @@ -4328,10 +4297,6 @@ packages: strip-hex-prefix: 1.0.0 dev: true - /object-inspect@1.12.2: - resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} - dev: false - /object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: false @@ -4345,8 +4310,8 @@ packages: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 + call-bind: 1.0.5 + define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 dev: false @@ -4887,8 +4852,8 @@ packages: /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.1.3 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 is-regex: 1.1.4 dev: false @@ -5006,8 +4971,8 @@ packages: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: call-bind: 1.0.5 - get-intrinsic: 1.1.3 - object-inspect: 1.12.2 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 dev: false /signal-exit@3.0.7: @@ -5461,9 +5426,9 @@ packages: engines: {node: '>=8'} dev: false - /ts-api-utils@1.0.3(typescript@5.4.5): - resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} - engines: {node: '>=16.13.0'} + /ts-api-utils@1.3.0(typescript@5.4.5): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' dependencies: @@ -5488,7 +5453,7 @@ packages: typescript: 5.4.5 dev: true - /ts-node@10.9.2(@types/node@20.12.11)(typescript@5.4.5): + /ts-node@10.9.2(@types/node@20.12.12)(typescript@5.4.5): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -5507,7 +5472,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 20.12.11 + '@types/node': 20.12.12 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -5672,7 +5637,7 @@ packages: /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 From 9b8e2528869ce03438757212efe80374842bdc9c Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Mon, 27 May 2024 11:58:43 +0200 Subject: [PATCH 04/19] Relayer ContractReader( chain reader) constructor (#13274) * Implement NewChainReader method for evm Relayer * Update relayer refs * Update relayer refs --- core/scripts/go.mod | 8 ++--- core/scripts/go.sum | 16 +++++----- .../ocr2/plugins/generic/relayerset_test.go | 4 +++ .../ocr2/plugins/mercury/plugin_test.go | 2 +- core/services/relay/evm/chain_reader.go | 2 +- core/services/relay/evm/chain_reader_test.go | 16 +++++++++- core/services/relay/evm/evm.go | 14 +++++++-- core/services/relay/evm/functions.go | 2 +- core/services/relay/evm/mercury_provider.go | 6 ++-- .../relay/evm/mocks/loop_relay_adapter.go | 30 +++++++++++++++++++ core/services/relay/evm/ocr2keeper.go | 2 +- core/services/relay/evm/ocr2vrf.go | 4 +-- core/services/relay/evm/plugin_provider.go | 6 ++-- core/web/testutils/mock_relayer.go | 4 +++ go.mod | 8 ++--- go.sum | 16 +++++----- integration-tests/go.mod | 10 ++++--- integration-tests/go.sum | 24 +++++++-------- integration-tests/load/go.mod | 10 +++---- integration-tests/load/go.sum | 16 +++++----- plugins/medianpoc/plugin_test.go | 2 +- 21 files changed, 132 insertions(+), 70 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 005807faafb..5185278e759 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -21,7 +21,7 @@ require ( github.com/prometheus/client_golang v1.17.0 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c @@ -256,11 +256,11 @@ require ( github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chain-selectors v1.0.10 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.8.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 89d9a3e158b..1e12ac7f3dc 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1185,18 +1185,18 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 h1:od+11B83s0mQwAMPP3lhtb0nYz63pIKpJEKddfFpu/M= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7/go.mod h1:cFHRblGbGn/rFYOOGsNbtLicMc1+5YdN0KoebYr93pk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 h1:Sec/GpBpUVaTEax1kSHlTvkzF/+d3w5roAQXaj5+SLA= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69/go.mod h1:ZQKf+0OLzCLYIisH/OdOIQuFRI6bDuw+jPBTATyHfFM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 h1:iyLE5c2YFxy89t2v5u+aQOHqRE4c+sCMze70KIo07mI= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9 h1:BiOkkTfgn2J0QwOqfuSm1uoojYgKmLqq9dUsq8EnBzs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9/go.mod h1:sR0dMjjpvvEpX3qH8DPRANauPkbO9jgUUGYK95xjLRU= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69 h1:ssh/w3oXWu+C6bE88GuFRC1+0Bx/4ihsbc80XMLrl2k= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69/go.mod h1:VsfjhvWgjxqWja4q+FlXEtX5lu8BSxn10xRo6gi948g= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 h1:ybdconEoRBHLwtDKlZKYaeanQ8UoVqdDiaTlPV+qEiI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 h1:LQmRsrzzaYYN3wEU1l5tWiccznhvbyGnu2N+wHSXZAo= github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= diff --git a/core/services/ocr2/plugins/generic/relayerset_test.go b/core/services/ocr2/plugins/generic/relayerset_test.go index 021a15c6eff..90a22411775 100644 --- a/core/services/ocr2/plugins/generic/relayerset_test.go +++ b/core/services/ocr2/plugins/generic/relayerset_test.go @@ -150,6 +150,10 @@ func (t *TestRelayer) Ready() error { panic("implement me") } func (t *TestRelayer) HealthReport() map[string]error { panic("implement me") } +func (t *TestRelayer) NewContractReader(_ context.Context, _ []byte) (types.ContractReader, error) { + panic("implement me") +} + func (t *TestRelayer) GetChainStatus(ctx context.Context) (types.ChainStatus, error) { panic("implement me") } diff --git a/core/services/ocr2/plugins/mercury/plugin_test.go b/core/services/ocr2/plugins/mercury/plugin_test.go index 1d3260fdb1f..d617d63116a 100644 --- a/core/services/ocr2/plugins/mercury/plugin_test.go +++ b/core/services/ocr2/plugins/mercury/plugin_test.go @@ -206,7 +206,7 @@ func newServicesTestWrapper(t *testing.T, pluginConfig job.JSONConfig, feedID ut type testProvider struct{} // ChainReader implements types.MercuryProvider. -func (*testProvider) ChainReader() commontypes.ChainReader { panic("unimplemented") } +func (*testProvider) ChainReader() commontypes.ContractReader { panic("unimplemented") } // Close implements types.MercuryProvider. func (*testProvider) Close() error { return nil } diff --git a/core/services/relay/evm/chain_reader.go b/core/services/relay/evm/chain_reader.go index 4a8c3691d1a..ad646249b7a 100644 --- a/core/services/relay/evm/chain_reader.go +++ b/core/services/relay/evm/chain_reader.go @@ -25,7 +25,7 @@ import ( type ChainReaderService interface { services.ServiceCtx - commontypes.ChainReader + commontypes.ContractReader } type chainReader struct { diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index 662d5258bfb..fc62022f8b5 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -254,7 +254,7 @@ func (it *chainReaderInterfaceTester) GetAccountBytes(i int) []byte { return account[:] } -func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes.ChainReader { +func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes.ContractReader { ctx := testutils.Context(t) if it.cr != nil { return it.cr @@ -271,6 +271,20 @@ func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes } lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), it.client, lggr, lpOpts) require.NoError(t, lp.Start(ctx)) + + // TODO uncomment this after this is fixed BCF-3242 + //chain := mocks.NewChain(t) + //chain.Mock.On("LogPoller").Return(lp) + //chain.Mock.On("ID").Return(it.client.ConfiguredChainID()) + // + //keyStore := cltest.NewKeyStore(t, db) + //relayer, err := evm.NewRelayer(lggr, chain, evm.RelayerOpts{DS: db, CSAETHKeystore: keyStore, CapabilitiesRegistry: capabilities.NewRegistry(lggr)}) + //require.NoError(t, err) + // + //cfgBytes, err := cbor.Marshal(it.chainConfig) + //require.NoError(t, err) + //cr, err := relayer.NewContractReader(cfgBytes) + cr, err := evm.NewChainReaderService(ctx, lggr, lp, it.client, it.chainConfig) require.NoError(t, err) require.NoError(t, cr.Start(ctx)) diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 5a0ccffaf71..3a115006a9f 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -78,7 +78,7 @@ type Relayer struct { lggr logger.Logger ks CSAETHKeystore mercuryPool wsrpc.Pool - chainReader commontypes.ChainReader + chainReader commontypes.ContractReader codec commontypes.Codec capabilitiesRegistry coretypes.CapabilitiesRegistry @@ -594,6 +594,16 @@ func newOnChainContractTransmitter(ctx context.Context, lggr logger.Logger, rarg ) } +func (r *Relayer) NewContractReader(chainReaderConfig []byte) (commontypes.ContractReader, error) { + ctx := context.Background() + cfg := &types.ChainReaderConfig{} + if err := json.Unmarshal(chainReaderConfig, cfg); err != nil { + return nil, fmt.Errorf("failed to unmarshall chain reader config err: %s", err) + } + + return NewChainReaderService(ctx, r.lggr, r.chain.LogPoller(), r.chain.Client(), *cfg) +} + func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.MedianProvider, error) { // TODO https://smartcontract-it.atlassian.net/browse/BCF-2887 ctx := context.Background() @@ -733,7 +743,7 @@ func (p *medianProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker return p.configWatcher.ContractConfigTracker() } -func (p *medianProvider) ChainReader() commontypes.ChainReader { +func (p *medianProvider) ChainReader() commontypes.ContractReader { return p.chainReader } diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index f10874da149..a04a991e379 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -81,7 +81,7 @@ func (p *functionsProvider) Name() string { return p.configWatcher.Name() } -func (p *functionsProvider) ChainReader() commontypes.ChainReader { +func (p *functionsProvider) ChainReader() commontypes.ContractReader { return nil } diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index 9159a13590e..48882b701c9 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -24,7 +24,7 @@ var _ commontypes.MercuryProvider = (*mercuryProvider)(nil) type mercuryProvider struct { cp commontypes.ConfigProvider - chainReader commontypes.ChainReader + chainReader commontypes.ContractReader codec commontypes.Codec transmitter evmmercury.Transmitter reportCodecV1 v1.ReportCodec @@ -37,7 +37,7 @@ type mercuryProvider struct { func NewMercuryProvider( cp commontypes.ConfigProvider, - chainReader commontypes.ChainReader, + chainReader commontypes.ContractReader, codec commontypes.Codec, mercuryChainReader mercurytypes.ChainReader, transmitter evmmercury.Transmitter, @@ -123,7 +123,7 @@ func (p *mercuryProvider) MercuryServerFetcher() mercurytypes.ServerFetcher { return p.transmitter } -func (p *mercuryProvider) ChainReader() commontypes.ChainReader { +func (p *mercuryProvider) ChainReader() commontypes.ContractReader { return p.chainReader } diff --git a/core/services/relay/evm/mocks/loop_relay_adapter.go b/core/services/relay/evm/mocks/loop_relay_adapter.go index 9b2fed8423a..273589709c5 100644 --- a/core/services/relay/evm/mocks/loop_relay_adapter.go +++ b/core/services/relay/evm/mocks/loop_relay_adapter.go @@ -196,6 +196,36 @@ func (_m *LoopRelayAdapter) NewConfigProvider(_a0 context.Context, _a1 types.Rel return r0, r1 } +// NewContractReader provides a mock function with given fields: ctx, contractReaderConfig +func (_m *LoopRelayAdapter) NewContractReader(ctx context.Context, contractReaderConfig []byte) (types.ChainReader, error) { + ret := _m.Called(ctx, contractReaderConfig) + + if len(ret) == 0 { + panic("no return value specified for NewContractReader") + } + + var r0 types.ChainReader + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []byte) (types.ChainReader, error)); ok { + return rf(ctx, contractReaderConfig) + } + if rf, ok := ret.Get(0).(func(context.Context, []byte) types.ChainReader); ok { + r0 = rf(ctx, contractReaderConfig) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.ChainReader) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []byte) error); ok { + r1 = rf(ctx, contractReaderConfig) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewLLOProvider provides a mock function with given fields: _a0, _a1, _a2 func (_m *LoopRelayAdapter) NewLLOProvider(_a0 context.Context, _a1 types.RelayArgs, _a2 types.PluginArgs) (types.LLOProvider, error) { ret := _m.Called(_a0, _a1, _a2) diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index d272ce5aa86..709ee97221e 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -196,7 +196,7 @@ func (c *ocr2keeperProvider) ContractTransmitter() ocrtypes.ContractTransmitter return c.contractTransmitter } -func (c *ocr2keeperProvider) ChainReader() commontypes.ChainReader { +func (c *ocr2keeperProvider) ChainReader() commontypes.ContractReader { return nil } diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index 3f9fb11bfc9..f8cf7527f2c 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -109,7 +109,7 @@ func (c *dkgProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return c.contractTransmitter } -func (c *dkgProvider) ChainReader() commontypes.ChainReader { +func (c *dkgProvider) ChainReader() commontypes.ContractReader { return nil } @@ -126,7 +126,7 @@ func (c *ocr2vrfProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return c.contractTransmitter } -func (c *ocr2vrfProvider) ChainReader() commontypes.ChainReader { +func (c *ocr2vrfProvider) ChainReader() commontypes.ContractReader { return nil } diff --git a/core/services/relay/evm/plugin_provider.go b/core/services/relay/evm/plugin_provider.go index a419d069cae..ffcea48db2c 100644 --- a/core/services/relay/evm/plugin_provider.go +++ b/core/services/relay/evm/plugin_provider.go @@ -13,7 +13,7 @@ import ( type pluginProvider struct { services.Service - chainReader types.ChainReader + chainReader types.ContractReader codec types.Codec contractTransmitter ocrtypes.ContractTransmitter configWatcher *configWatcher @@ -24,7 +24,7 @@ type pluginProvider struct { var _ types.PluginProvider = (*pluginProvider)(nil) func NewPluginProvider( - chainReader types.ChainReader, + chainReader types.ContractReader, codec types.Codec, contractTransmitter ocrtypes.ContractTransmitter, configWatcher *configWatcher, @@ -62,7 +62,7 @@ func (p *pluginProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker return p.configWatcher.configPoller } -func (p *pluginProvider) ChainReader() types.ChainReader { +func (p *pluginProvider) ChainReader() types.ContractReader { return p.chainReader } diff --git a/core/web/testutils/mock_relayer.go b/core/web/testutils/mock_relayer.go index d6a44a2379d..1cf9cfc1445 100644 --- a/core/web/testutils/mock_relayer.go +++ b/core/web/testutils/mock_relayer.go @@ -32,6 +32,10 @@ func (m MockRelayer) HealthReport() map[string]error { panic("not implemented") } +func (m MockRelayer) NewContractReader(_ context.Context, _ []byte) (commontypes.ContractReader, error) { + panic("not implemented") +} + func (m MockRelayer) GetChainStatus(ctx context.Context) (commontypes.ChainStatus, error) { return m.ChainStatus, nil } diff --git a/go.mod b/go.mod index 34fe808a821..28e85e2c97c 100644 --- a/go.mod +++ b/go.mod @@ -72,12 +72,12 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9 - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 diff --git a/go.sum b/go.sum index d76c84b55c2..0671117cf0c 100644 --- a/go.sum +++ b/go.sum @@ -1171,18 +1171,18 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 h1:od+11B83s0mQwAMPP3lhtb0nYz63pIKpJEKddfFpu/M= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7/go.mod h1:cFHRblGbGn/rFYOOGsNbtLicMc1+5YdN0KoebYr93pk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 h1:Sec/GpBpUVaTEax1kSHlTvkzF/+d3w5roAQXaj5+SLA= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69/go.mod h1:ZQKf+0OLzCLYIisH/OdOIQuFRI6bDuw+jPBTATyHfFM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 h1:iyLE5c2YFxy89t2v5u+aQOHqRE4c+sCMze70KIo07mI= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9 h1:BiOkkTfgn2J0QwOqfuSm1uoojYgKmLqq9dUsq8EnBzs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9/go.mod h1:sR0dMjjpvvEpX3qH8DPRANauPkbO9jgUUGYK95xjLRU= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69 h1:ssh/w3oXWu+C6bE88GuFRC1+0Bx/4ihsbc80XMLrl2k= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69/go.mod h1:VsfjhvWgjxqWja4q+FlXEtX5lu8BSxn10xRo6gi948g= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 h1:ybdconEoRBHLwtDKlZKYaeanQ8UoVqdDiaTlPV+qEiI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index fb35ea9b548..3733a827ed1 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -27,7 +27,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 github.com/smartcontractkit/chainlink-testing-framework v1.28.15 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 @@ -225,7 +225,9 @@ require ( github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect + github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -373,11 +375,11 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chain-selectors v1.0.10 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 // indirect github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f6ad694c39f..06383fe3a59 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -813,8 +813,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -823,8 +823,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= @@ -1512,18 +1512,18 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 h1:od+11B83s0mQwAMPP3lhtb0nYz63pIKpJEKddfFpu/M= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7/go.mod h1:cFHRblGbGn/rFYOOGsNbtLicMc1+5YdN0KoebYr93pk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 h1:Sec/GpBpUVaTEax1kSHlTvkzF/+d3w5roAQXaj5+SLA= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69/go.mod h1:ZQKf+0OLzCLYIisH/OdOIQuFRI6bDuw+jPBTATyHfFM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 h1:iyLE5c2YFxy89t2v5u+aQOHqRE4c+sCMze70KIo07mI= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9 h1:BiOkkTfgn2J0QwOqfuSm1uoojYgKmLqq9dUsq8EnBzs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9/go.mod h1:sR0dMjjpvvEpX3qH8DPRANauPkbO9jgUUGYK95xjLRU= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69 h1:ssh/w3oXWu+C6bE88GuFRC1+0Bx/4ihsbc80XMLrl2k= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69/go.mod h1:VsfjhvWgjxqWja4q+FlXEtX5lu8BSxn10xRo6gi948g= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 h1:ybdconEoRBHLwtDKlZKYaeanQ8UoVqdDiaTlPV+qEiI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-testing-framework v1.28.15 h1:mga7N6jtXQ3UOCt43IdsEnCMBh9xjOWPaE9BiM6kr6Q= github.com/smartcontractkit/chainlink-testing-framework v1.28.15/go.mod h1:x1zDOz8zcLjEvs9fNA9y/DMguLam/2+CJdpxX0+rM8A= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 h1:fX/xmGm1GBsD1ZZnooNT+eWA0hiTAqFlHzOC5CY4dy8= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index fa37320995f..b50577120af 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -16,7 +16,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 github.com/smartcontractkit/chainlink-testing-framework v1.28.15 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 @@ -206,10 +206,8 @@ require ( github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect - github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -366,11 +364,11 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chain-selectors v1.0.10 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 // indirect github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 // indirect github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index ae5193f7769..c50f8f40fc5 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1502,18 +1502,18 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 h1:od+11B83s0mQwAMPP3lhtb0nYz63pIKpJEKddfFpu/M= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7/go.mod h1:cFHRblGbGn/rFYOOGsNbtLicMc1+5YdN0KoebYr93pk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 h1:Sec/GpBpUVaTEax1kSHlTvkzF/+d3w5roAQXaj5+SLA= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69/go.mod h1:ZQKf+0OLzCLYIisH/OdOIQuFRI6bDuw+jPBTATyHfFM= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 h1:iyLE5c2YFxy89t2v5u+aQOHqRE4c+sCMze70KIo07mI= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9 h1:BiOkkTfgn2J0QwOqfuSm1uoojYgKmLqq9dUsq8EnBzs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524131846-a10ff1f030c9/go.mod h1:sR0dMjjpvvEpX3qH8DPRANauPkbO9jgUUGYK95xjLRU= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69 h1:ssh/w3oXWu+C6bE88GuFRC1+0Bx/4ihsbc80XMLrl2k= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240508155030-1024f2b55c69/go.mod h1:VsfjhvWgjxqWja4q+FlXEtX5lu8BSxn10xRo6gi948g= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 h1:ybdconEoRBHLwtDKlZKYaeanQ8UoVqdDiaTlPV+qEiI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-testing-framework v1.28.15 h1:mga7N6jtXQ3UOCt43IdsEnCMBh9xjOWPaE9BiM6kr6Q= github.com/smartcontractkit/chainlink-testing-framework v1.28.15/go.mod h1:x1zDOz8zcLjEvs9fNA9y/DMguLam/2+CJdpxX0+rM8A= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 h1:fX/xmGm1GBsD1ZZnooNT+eWA0hiTAqFlHzOC5CY4dy8= diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go index 673c1fabc7d..22e4c095d6c 100644 --- a/plugins/medianpoc/plugin_test.go +++ b/plugins/medianpoc/plugin_test.go @@ -73,7 +73,7 @@ func (p provider) OnchainConfigCodec() median.OnchainConfigCodec { return mockOnchainConfigCodec{} } -func (p provider) ChainReader() types.ChainReader { +func (p provider) ChainReader() types.ContractReader { return nil } From 96304756a77cdb2acf251d21d59b6aa8b55bf61a Mon Sep 17 00:00:00 2001 From: Christopher Dimitri Sastropranoto Date: Mon, 27 May 2024 17:29:25 +0700 Subject: [PATCH 05/19] CapabilityRegistry updates (#13183) * encode instead of encode packed for capability id * add config count to DON struct * address PR feedback * Update gethwrappers * update node signer type * address naming suggestions * implement remove DONs in capability registry * address PR feedback * add check * regen wrappers * address PR feedback * implement modify DON * add changeset * address PR feedback * add test for capability ID generation * update ICapabilityConfiguration interface * add getters for entities * address PR feedback * remove unused increment * address PR feedback * Update gethwrappers * address PR feedback --------- Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> --- .changeset/cool-readers-try.md | 5 + .changeset/curvy-pandas-wonder.md | 5 + .changeset/giant-hats-talk.md | 5 + .changeset/metal-horses-count.md | 5 + .changeset/thin-schools-own.md | 5 + .changeset/two-papayas-begin.md | 5 + contracts/.changeset/hip-comics-rhyme.md | 5 + contracts/.changeset/long-beans-turn.md | 5 + contracts/.changeset/quiet-guests-march.md | 5 + contracts/.changeset/selfish-files-learn.md | 5 + contracts/.changeset/six-ears-sin.md | 5 + contracts/.changeset/sour-parents-explode.md | 5 + .../src/v0.8/keystone/CapabilityRegistry.sol | 404 ++++++++++++++---- .../interfaces/ICapabilityConfiguration.sol | 15 +- ...tyRegistry_GetHashedCapabilityIdTest.t.sol | 28 ++ .../test/CapabilityRegistry_AddDONTest.t.sol | 80 ++-- ...abilityRegistry_AddNodeOperatorsTest.t.sol | 15 +- .../CapabilityRegistry_AddNodesTest.t.sol | 79 ++-- .../test/CapabilityRegistry_GetDONsTest.t.sol | 93 ++++ ...abilityRegistry_GetNodeOperatorsTest.t.sol | 40 ++ .../CapabilityRegistry_GetNodesTest.t.sol | 80 ++++ .../CapabilityRegistry_RemoveDONsTest.t.sol | 95 ++++ ...lityRegistry_RemoveNodeOperatorsTest.t.sol | 15 +- .../CapabilityRegistry_RemoveNodesTest.t.sol | 47 +- .../CapabilityRegistry_UpdateDONTest.t.sol | 224 ++++++++++ .../CapabilityRegistry_UpdateNodesTest.t.sol | 121 ++++-- .../src/v0.8/keystone/test/Constants.t.sol | 4 +- .../mocks/CapabilityConfigurationContract.sol | 9 +- .../keystone_capability_registry.go | 231 +++++++--- ...rapper-dependency-versions-do-not-edit.txt | 2 +- 30 files changed, 1374 insertions(+), 268 deletions(-) create mode 100644 .changeset/cool-readers-try.md create mode 100644 .changeset/curvy-pandas-wonder.md create mode 100644 .changeset/giant-hats-talk.md create mode 100644 .changeset/metal-horses-count.md create mode 100644 .changeset/thin-schools-own.md create mode 100644 .changeset/two-papayas-begin.md create mode 100644 contracts/.changeset/hip-comics-rhyme.md create mode 100644 contracts/.changeset/long-beans-turn.md create mode 100644 contracts/.changeset/quiet-guests-march.md create mode 100644 contracts/.changeset/selfish-files-learn.md create mode 100644 contracts/.changeset/six-ears-sin.md create mode 100644 contracts/.changeset/sour-parents-explode.md create mode 100644 contracts/src/v0.8/keystone/test/CapabiityRegistry_GetHashedCapabilityIdTest.t.sol create mode 100644 contracts/src/v0.8/keystone/test/CapabilityRegistry_GetDONsTest.t.sol create mode 100644 contracts/src/v0.8/keystone/test/CapabilityRegistry_GetNodeOperatorsTest.t.sol create mode 100644 contracts/src/v0.8/keystone/test/CapabilityRegistry_GetNodesTest.t.sol create mode 100644 contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveDONsTest.t.sol create mode 100644 contracts/src/v0.8/keystone/test/CapabilityRegistry_UpdateDONTest.t.sol diff --git a/.changeset/cool-readers-try.md b/.changeset/cool-readers-try.md new file mode 100644 index 00000000000..5650b7097a3 --- /dev/null +++ b/.changeset/cool-readers-try.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal add modify DON function to capability registry diff --git a/.changeset/curvy-pandas-wonder.md b/.changeset/curvy-pandas-wonder.md new file mode 100644 index 00000000000..13d869642aa --- /dev/null +++ b/.changeset/curvy-pandas-wonder.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal add getters in capability registry diff --git a/.changeset/giant-hats-talk.md b/.changeset/giant-hats-talk.md new file mode 100644 index 00000000000..bfe57f487a3 --- /dev/null +++ b/.changeset/giant-hats-talk.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal update ICapabilityConfiguration interface diff --git a/.changeset/metal-horses-count.md b/.changeset/metal-horses-count.md new file mode 100644 index 00000000000..5cb5e8331a9 --- /dev/null +++ b/.changeset/metal-horses-count.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal implement remove DONs in capability registry diff --git a/.changeset/thin-schools-own.md b/.changeset/thin-schools-own.md new file mode 100644 index 00000000000..875063e747e --- /dev/null +++ b/.changeset/thin-schools-own.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal track config count in DON struct diff --git a/.changeset/two-papayas-begin.md b/.changeset/two-papayas-begin.md new file mode 100644 index 00000000000..3487a1b6b82 --- /dev/null +++ b/.changeset/two-papayas-begin.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal update node signer type diff --git a/contracts/.changeset/hip-comics-rhyme.md b/contracts/.changeset/hip-comics-rhyme.md new file mode 100644 index 00000000000..f1142821487 --- /dev/null +++ b/contracts/.changeset/hip-comics-rhyme.md @@ -0,0 +1,5 @@ +--- +"@chainlink/contracts": patch +--- + +track config count in DON struct diff --git a/contracts/.changeset/long-beans-turn.md b/contracts/.changeset/long-beans-turn.md new file mode 100644 index 00000000000..3af5e4a2171 --- /dev/null +++ b/contracts/.changeset/long-beans-turn.md @@ -0,0 +1,5 @@ +--- +"@chainlink/contracts": patch +--- + +implement remove DONs in capability registry diff --git a/contracts/.changeset/quiet-guests-march.md b/contracts/.changeset/quiet-guests-march.md new file mode 100644 index 00000000000..40285829c1a --- /dev/null +++ b/contracts/.changeset/quiet-guests-march.md @@ -0,0 +1,5 @@ +--- +"@chainlink/contracts": patch +--- + +Add update DON function to capability registry diff --git a/contracts/.changeset/selfish-files-learn.md b/contracts/.changeset/selfish-files-learn.md new file mode 100644 index 00000000000..3d348d1d8d7 --- /dev/null +++ b/contracts/.changeset/selfish-files-learn.md @@ -0,0 +1,5 @@ +--- +"@chainlink/contracts": patch +--- + +add getters in capability registry diff --git a/contracts/.changeset/six-ears-sin.md b/contracts/.changeset/six-ears-sin.md new file mode 100644 index 00000000000..186481a36e6 --- /dev/null +++ b/contracts/.changeset/six-ears-sin.md @@ -0,0 +1,5 @@ +--- +"@chainlink/contracts": patch +--- + +update ICapabilityConfiguration interface diff --git a/contracts/.changeset/sour-parents-explode.md b/contracts/.changeset/sour-parents-explode.md new file mode 100644 index 00000000000..ff5b52b98fe --- /dev/null +++ b/contracts/.changeset/sour-parents-explode.md @@ -0,0 +1,5 @@ +--- +"@chainlink/contracts": patch +--- + +update node signer type diff --git a/contracts/src/v0.8/keystone/CapabilityRegistry.sol b/contracts/src/v0.8/keystone/CapabilityRegistry.sol index bb4871c20c5..9fe91ebf467 100644 --- a/contracts/src/v0.8/keystone/CapabilityRegistry.sol +++ b/contracts/src/v0.8/keystone/CapabilityRegistry.sol @@ -13,6 +13,7 @@ import {ICapabilityConfiguration} from "./interfaces/ICapabilityConfiguration.so contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { // Add the library methods using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.UintSet; struct NodeOperator { /// @notice The address of the admin that can manage a node @@ -22,11 +23,11 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { string name; } - struct NodeParams { + struct NodeInfo { /// @notice The id of the node operator that manages this node uint32 nodeOperatorId; /// @notice The signer address for application-layer message verification. - address signer; + bytes32 signer; /// @notice This is an Ed25519 public key that is used to identify a node. /// This key is guaranteed to be unique in the CapabilityRegistry. It is /// used to identify a node in the the P2P network. @@ -42,7 +43,13 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @notice The number of times the node's capability has been updated uint32 configCount; /// @notice The signer address for application-layer message verification. - address signer; + /// @dev This key is guaranteed to be unique in the CapabilityRegistry + /// as a signer address can only belong to one node. + /// @dev This should be the ABI encoded version of the node's address. + /// I.e 0x0000address. The Capability Registry does not store it as an address so that + /// non EVM chains with addresses greater than 20 bytes can be supported + /// in the future. + bytes32 signer; /// @notice This is an Ed25519 public key that is used to identify a node. /// This key is guaranteed to be unique in the CapabilityRegistry. It is /// used to identify a node in the the P2P network. @@ -54,7 +61,7 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { mapping(uint32 configCount => EnumerableSet.Bytes32Set capabilityId) supportedCapabilityIds; } - // CapabilityResponseType indicates whether remote response requires + /// @notice CapabilityResponseType indicates whether remote response requires // aggregation or is an already aggregated report. There are multiple // possible ways to aggregate. enum CapabilityResponseType { @@ -65,29 +72,29 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { } struct Capability { - // The `labelledName` is a partially qualified ID for the capability. - // - // Given the following capability ID: {name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}@{version} + /// @notice The partially qualified ID for the capability. + /// + /// @dev Given the following capability ID: {name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}@{version} // Then we denote the `labelledName` as the `{name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}` portion of the ID. - // - // Ex. id = "data-streams-reports:chain:ethereum@1.0.0" - // labelledName = "data-streams-reports:chain:ethereum" - // - // bytes32(string); validation regex: ^[a-z0-9_\-:]{1,32}$ + /// + /// Ex. id = "data-streams-reports:chain:ethereum@1.0.0" + /// labelledName = "data-streams-reports:chain:ethereum" + /// + /// bytes32(string); validation regex: ^[a-z0-9_\-:]{1,32}$ bytes32 labelledName; - // Semver, e.g., "1.2.3" - // bytes32(string); must be valid Semver + max 32 characters. + /// @notice Semver, e.g., "1.2.3" + /// @dev must be valid Semver + max 32 characters. bytes32 version; - // responseType indicates whether remote response requires + /// @notice Indicates whether remote response requires // aggregation or is an OCR report. There are multiple possible // ways to aggregate. CapabilityResponseType responseType; - // An address to the capability configuration contract. Having this defined + /// @notice An address to the capability configuration contract. Having this defined // on a capability enforces consistent configuration across DON instances // serving the same capability. Configuration contract MUST implement // CapabilityConfigurationContractInterface. // - // The main use cases are: + /// @dev The main use cases are: // 1) Sharing capability configuration across DON instances // 2) Inspect and modify on-chain configuration without off-chain // capability code. @@ -107,21 +114,42 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { bytes config; } + struct DONCapabilityConfig { + /// @notice The set of p2pIds of nodes that belong to this DON. A node (the same + // p2pId) can belong to multiple DONs. + EnumerableSet.Bytes32Set nodes; + /// @notice The set of capabilityIds + bytes32[] capabilityIds; + /// @notice Mapping from hashed capability IDs to configs + mapping(bytes32 capabilityId => bytes config) capabilityConfigs; + } + /// @notice DON (Decentralized Oracle Network) is a grouping of nodes that support // the same capabilities. struct DON { /// @notice Computed. Auto-increment. uint32 id; + /// @notice The number of times the DON was configured + uint32 configCount; /// @notice True if the DON is public. A public DON means that it accepts /// external capability requests bool isPublic; - /// @notice The set of p2pIds of nodes that belong to this DON. A node (the same - // p2pId) can belong to multiple DONs. - EnumerableSet.Bytes32Set nodes; - /// @notice The set of capabilityIds - EnumerableSet.Bytes32Set capabilityIds; - /// @notice Mapping from hashed capability IDs to configs - mapping(bytes32 capabilityId => bytes config) capabilityConfigs; + /// @notice Mapping of config counts to configurations + mapping(uint32 configCount => DONCapabilityConfig donConfig) config; + } + + struct DONInfo { + /// @notice Computed. Auto-increment. + uint32 id; + /// @notice The number of times the DON was configured + uint32 configCount; + /// @notice True if the DON is public. A public DON means that it accepts + /// external capability requests + bool isPublic; + /// @notice List of member node P2P Ids + bytes32[] nodeP2PIds; + /// @notice List of capability configurations + CapabilityConfiguration[] capabilityConfigurations; } /// @notice This error is thrown when a caller is not allowed @@ -151,7 +179,8 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @notice This event is emitted when a new node is added /// @param p2pId The P2P ID of the node /// @param nodeOperatorId The ID of the node operator that manages this node - event NodeAdded(bytes32 p2pId, uint256 nodeOperatorId); + /// @param signer The encoded node's signer address + event NodeAdded(bytes32 p2pId, uint256 nodeOperatorId, bytes32 signer); /// @notice This event is emitted when a node is removed /// @param p2pId The P2P ID of the node that was removed @@ -161,15 +190,21 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @param p2pId The P2P ID of the node /// @param nodeOperatorId The ID of the node operator that manages this node /// @param signer The node's signer address - event NodeUpdated(bytes32 p2pId, uint256 nodeOperatorId, address signer); + event NodeUpdated(bytes32 p2pId, uint256 nodeOperatorId, bytes32 signer); - /// @notice This event is emitted when a new DON is created - /// @param donId The ID of the newly created DON - /// @param isPublic True if the newly created DON is public - event DONAdded(uint256 donId, bool isPublic); + /// @notice This event is emitted when a DON's config is set + /// @param donId The ID of the DON the config was set for + /// @param configCount The number of times the DON has been + /// configured + event ConfigSet(uint32 donId, uint32 configCount); + + /// @notice This error is emitted when a DON does not exist + /// @param donId The ID of the nonexistent DON + error DONDoesNotExist(uint32 donId); /// @notice This error is thrown when trying to set the node's - /// signer address to zero + /// signer address to zero or if the signer address has already + /// been used by another node error InvalidNodeSigner(); /// @notice This error is thrown when trying add a capability that already @@ -244,6 +279,18 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { /// Deprecated capabilities are skipped by the `getCapabilities` function. EnumerableSet.Bytes32Set private s_deprecatedHashedCapabilityIds; + /// @notice Set of removed Node Operator IDs + EnumerableSet.UintSet private s_removedNodeOperatorIds; + + /// @notice Set of removed DON IDs + EnumerableSet.UintSet private s_removedDONIds; + + /// @notice Encoded node signer addresses + EnumerableSet.Bytes32Set private s_nodeSigners; + + /// @notice Set of node P2P IDs + EnumerableSet.Bytes32Set private s_nodeP2PIds; + /// @notice Mapping of node operators mapping(uint256 nodeOperatorId => NodeOperator nodeOperator) private s_nodeOperators; @@ -254,10 +301,12 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { mapping(uint32 donId => DON don) private s_dons; /// @notice The latest node operator ID + // @dev Starting with 1 to avoid confusion with the zero value /// @dev No getter for this as this is an implementation detail - uint256 private s_nodeOperatorId; + uint32 private s_nodeOperatorId = 1; - /// @notice Starting with 1 to avoid confusion with the zero value. + /// @notice The latest DON ID + /// @dev Starting with 1 to avoid confusion with the zero value /// @dev No getter for this as this is an implementation detail uint32 private s_donId = 1; @@ -284,6 +333,7 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { for (uint256 i; i < nodeOperatorIds.length; ++i) { uint256 nodeOperatorId = nodeOperatorIds[i]; delete s_nodeOperators[nodeOperatorId]; + s_removedNodeOperatorIds.add(nodeOperatorId); emit NodeOperatorRemoved(nodeOperatorId); } } @@ -319,22 +369,38 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { return s_nodeOperators[nodeOperatorId]; } + /// @notice Gets all node operators + /// @return NodeOperator[] All node operators + function getNodeOperators() external view returns (NodeOperator[] memory) { + uint32 nodeOperatorId = s_nodeOperatorId; + /// Minus one to account for s_nodeOperatorId starting at index 1 + NodeOperator[] memory nodeOperators = new NodeOperator[](s_nodeOperatorId - s_removedNodeOperatorIds.length() - 1); + uint256 idx; + for (uint256 i = 1; i < nodeOperatorId; ++i) { + if (!s_removedNodeOperatorIds.contains(i)) { + nodeOperators[idx] = s_nodeOperators[i]; + ++idx; + } + } + return nodeOperators; + } + /// @notice Adds nodes. Nodes can be added with deprecated capabilities to /// avoid breaking changes when deprecating capabilities. /// @param nodes The nodes to add - function addNodes(NodeParams[] calldata nodes) external { + function addNodes(NodeInfo[] calldata nodes) external { for (uint256 i; i < nodes.length; ++i) { - NodeParams memory node = nodes[i]; + NodeInfo memory node = nodes[i]; bool isOwner = msg.sender == owner(); NodeOperator memory nodeOperator = s_nodeOperators[node.nodeOperatorId]; if (!isOwner && msg.sender != nodeOperator.admin) revert AccessForbidden(); - bool nodeExists = s_nodes[node.p2pId].signer != address(0); + bool nodeExists = s_nodes[node.p2pId].signer != bytes32(""); if (nodeExists || bytes32(node.p2pId) == bytes32("")) revert InvalidNodeP2PId(node.p2pId); - if (node.signer == address(0)) revert InvalidNodeSigner(); + if (bytes32(node.signer) == bytes32("") || s_nodeSigners.contains(node.signer)) revert InvalidNodeSigner(); bytes32[] memory capabilityIds = node.hashedCapabilityIds; if (capabilityIds.length == 0) revert InvalidNodeCapabilities(capabilityIds); @@ -350,7 +416,9 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { s_nodes[node.p2pId].nodeOperatorId = node.nodeOperatorId; s_nodes[node.p2pId].p2pId = node.p2pId; s_nodes[node.p2pId].signer = node.signer; - emit NodeAdded(node.p2pId, node.nodeOperatorId); + s_nodeSigners.add(node.signer); + s_nodeP2PIds.add(node.p2pId); + emit NodeAdded(node.p2pId, node.nodeOperatorId, node.signer); } } @@ -362,12 +430,14 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { for (uint256 i; i < removedNodeP2PIds.length; ++i) { bytes32 p2pId = removedNodeP2PIds[i]; - bool nodeExists = s_nodes[p2pId].signer != address(0); + bool nodeExists = bytes32(s_nodes[p2pId].signer) != bytes32(""); if (!nodeExists) revert InvalidNodeP2PId(p2pId); NodeOperator memory nodeOperator = s_nodeOperators[s_nodes[p2pId].nodeOperatorId]; if (!isOwner && msg.sender != nodeOperator.admin) revert AccessForbidden(); + s_nodeSigners.remove(s_nodes[p2pId].signer); + s_nodeP2PIds.remove(s_nodes[p2pId].p2pId); delete s_nodes[p2pId]; emit NodeRemoved(p2pId); } @@ -376,19 +446,22 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @notice Updates nodes. The node admin can update the node's signer address /// and reconfigure its supported capabilities /// @param nodes The nodes to update - function updateNodes(NodeParams[] calldata nodes) external { + function updateNodes(NodeInfo[] calldata nodes) external { for (uint256 i; i < nodes.length; ++i) { - NodeParams memory node = nodes[i]; + NodeInfo memory node = nodes[i]; bool isOwner = msg.sender == owner(); NodeOperator memory nodeOperator = s_nodeOperators[node.nodeOperatorId]; if (!isOwner && msg.sender != nodeOperator.admin) revert AccessForbidden(); - bool nodeExists = s_nodes[node.p2pId].signer != address(0); + bool nodeExists = s_nodes[node.p2pId].signer != bytes32(""); if (!nodeExists) revert InvalidNodeP2PId(node.p2pId); - if (node.signer == address(0)) revert InvalidNodeSigner(); + if ( + bytes32(node.signer) == bytes32("") || + (s_nodes[node.p2pId].signer != node.signer && s_nodeSigners.contains(node.signer)) + ) revert InvalidNodeSigner(); bytes32[] memory supportedCapabilityIds = node.hashedCapabilityIds; if (supportedCapabilityIds.length == 0) revert InvalidNodeCapabilities(supportedCapabilityIds); @@ -403,27 +476,45 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { s_nodes[node.p2pId].nodeOperatorId = node.nodeOperatorId; s_nodes[node.p2pId].p2pId = node.p2pId; - s_nodes[node.p2pId].signer = node.signer; + + bytes32 previousSigner = s_nodes[node.p2pId].signer; + + if (s_nodes[node.p2pId].signer != node.signer) { + s_nodeSigners.remove(previousSigner); + s_nodes[node.p2pId].signer = node.signer; + s_nodeSigners.add(node.signer); + } emit NodeUpdated(node.p2pId, node.nodeOperatorId, node.signer); } } /// @notice Gets a node's data /// @param p2pId The P2P ID of the node to query for - /// @return NodeParams The node data + /// @return NodeInfo The node data /// @return configCount The number of times the node has been configured - function getNode(bytes32 p2pId) external view returns (NodeParams memory, uint32 configCount) { - return ( - NodeParams({ - nodeOperatorId: s_nodes[p2pId].nodeOperatorId, - p2pId: s_nodes[p2pId].p2pId, - signer: s_nodes[p2pId].signer, - hashedCapabilityIds: s_nodes[p2pId].supportedCapabilityIds[s_nodes[p2pId].configCount].values() - }), - s_nodes[p2pId].configCount - ); + function getNode(bytes32 p2pId) external view returns (NodeInfo memory, uint32 configCount) { + return _getNode(p2pId); + } + + /// @notice Gets all nodes + /// @return NodeInfo[] All nodes in the capability registry + /// @return uint32[] All the config counts for the nodes in the capability registry + function getNodes() external view returns (NodeInfo[] memory, uint32[] memory) { + bytes32[] memory p2pIds = s_nodeP2PIds.values(); + NodeInfo[] memory nodeInfo = new NodeInfo[](p2pIds.length); + uint32[] memory configCounts = new uint32[](p2pIds.length); + + for (uint256 i; i < p2pIds.length; ++i) { + bytes32 p2pId = p2pIds[i]; + (NodeInfo memory node, uint32 configCount) = _getNode(p2pId); + nodeInfo[i] = node; + configCounts[i] = configCount; + } + return (nodeInfo, configCounts); } + /// @notice Adds a new capability to the capability registry + /// @param capability The capability being added function addCapability(Capability calldata capability) external onlyOwner { bytes32 hashedId = getHashedCapabilityId(capability.labelledName, capability.version); if (s_hashedCapabilityIds.contains(hashedId)) revert CapabilityAlreadyExists(); @@ -432,7 +523,8 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { if ( capability.configurationContract.code.length == 0 || !IERC165(capability.configurationContract).supportsInterface( - ICapabilityConfiguration.getCapabilityConfiguration.selector + ICapabilityConfiguration.getCapabilityConfiguration.selector ^ + ICapabilityConfiguration.beforeCapabilityConfigSet.selector ) ) revert InvalidCapabilityConfigurationContractInterface(capability.configurationContract); } @@ -454,7 +546,7 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { } /// @notice This function returns a Capability by its hashed ID. Use `getHashedCapabilityId` to get the hashed ID. - function getCapability(bytes32 hashedId) public view returns (Capability memory) { + function getCapability(bytes32 hashedId) external view returns (Capability memory) { return s_capabilities[hashedId]; } @@ -479,7 +571,7 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { bytes32 hashedCapabilityId = hashedCapabilityIds[i]; if (!s_deprecatedHashedCapabilityIds.contains(hashedCapabilityId)) { - capabilities[newIndex] = getCapability(hashedCapabilityId); + capabilities[newIndex] = s_capabilities[hashedCapabilityId]; newIndex++; } } @@ -488,7 +580,10 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { } /// @notice This functions returns a capability id that has been hashed to fit into a bytes32 for cheaper access + /// @param labelledName The name of the capability + /// @param version The capability's version number /// @return bytes32 A unique identifier for the capability + /// @dev The hash of the encoded labelledName and version function getHashedCapabilityId(bytes32 labelledName, bytes32 version) public pure returns (bytes32) { return keccak256(abi.encodePacked(labelledName, version)); } @@ -512,68 +607,191 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface { bool isPublic ) external onlyOwner { uint32 id = s_donId; - s_dons[id].id = id; - s_dons[id].isPublic = isPublic; + _setDONConfig(id, 1, nodes, capabilityConfigurations, isPublic); + ++s_donId; + } - for (uint256 i; i < nodes.length; ++i) { - bytes32 nodeP2PId = nodes[i]; - if (s_dons[id].nodes.contains(nodeP2PId)) revert DuplicateDONNode(id, nodeP2PId); + /// @notice Updates a DON's configuration. This allows + /// the admin to reconfigure the list of capabilities supported + /// by the DON, the list of nodes that make up the DON as well + /// as whether or not the DON can accept external workflows + /// @param nodes The nodes making up the DON + /// @param capabilityConfigurations The list of configurations for the + /// capabilities supported by the DON + /// @param isPublic True if the DON is can accept external workflows + function updateDON( + uint32 donId, + bytes32[] calldata nodes, + CapabilityConfiguration[] calldata capabilityConfigurations, + bool isPublic + ) external onlyOwner { + uint32 configCount = s_dons[donId].configCount; + if (configCount == 0) revert DONDoesNotExist(donId); + _setDONConfig(donId, ++configCount, nodes, capabilityConfigurations, isPublic); + } + + /// @notice Removes DONs from the Capability Registry + /// @param donIds The IDs of the DON to be removed + function removeDONs(uint32[] calldata donIds) external onlyOwner { + for (uint256 i; i < donIds.length; ++i) { + uint32 donId = donIds[i]; + DON storage don = s_dons[donId]; + + // DON config count starts at index 1 + if (don.configCount == 0) revert DONDoesNotExist(donId); + delete s_dons[donId]; + s_removedDONIds.add(donId); + emit ConfigSet(donId, 0); + } + } + + /// @notice Gets DON's data + /// @param donId The DON ID + /// @return DONInfo The DON's parameters + function getDON(uint32 donId) external view returns (DONInfo memory) { + return _getDON(donId); + } + + /// @notice Returns the list of configured DONs + /// @return DONInfo[] The list of configured DONs + function getDONs() external view returns (DONInfo[] memory) { + /// Minus one to account for s_donId starting at index 1 + uint256 donId = s_donId; + DONInfo[] memory dons = new DONInfo[](s_donId - s_removedDONIds.length() - 1); + uint256 idx; + /// + for (uint32 i = 1; i < donId; ++i) { + if (!s_removedDONIds.contains(i)) { + dons[idx] = _getDON(i); + ++idx; + } + } + return dons; + } + + /// @notice Returns the DON specific configuration for a capability + /// @param donId The DON's ID + /// @param capabilityId The Capability ID + /// @return bytes The DON specific configuration for the capability + function getDONCapabilityConfig(uint32 donId, bytes32 capabilityId) external view returns (bytes memory) { + uint32 configCount = s_dons[donId].configCount; + return s_dons[donId].config[configCount].capabilityConfigs[capabilityId]; + } + + /// @notice Sets the configuration for a DON + /// @param donId The ID of the DON to set the configuration for + /// @param configCount The number of times the DON has been configured + /// @param nodes The nodes making up the DON + /// @param capabilityConfigurations The list of configurations for the + /// capabilities supported by the DON + /// @param isPublic True if the DON is can accept external workflows + function _setDONConfig( + uint32 donId, + uint32 configCount, + bytes32[] calldata nodes, + CapabilityConfiguration[] calldata capabilityConfigurations, + bool isPublic + ) internal { + DONCapabilityConfig storage donCapabilityConfig = s_dons[donId].config[configCount]; - s_dons[id].nodes.add(nodeP2PId); + for (uint256 i; i < nodes.length; ++i) { + if (donCapabilityConfig.nodes.contains(nodes[i])) revert DuplicateDONNode(donId, nodes[i]); + donCapabilityConfig.nodes.add(nodes[i]); } for (uint256 i; i < capabilityConfigurations.length; ++i) { CapabilityConfiguration calldata configuration = capabilityConfigurations[i]; - bytes32 capabilityId = configuration.capabilityId; - if (!s_hashedCapabilityIds.contains(capabilityId)) revert CapabilityDoesNotExist(capabilityId); - if (s_deprecatedHashedCapabilityIds.contains(capabilityId)) revert CapabilityIsDeprecated(capabilityId); + if (!s_hashedCapabilityIds.contains(configuration.capabilityId)) + revert CapabilityDoesNotExist(configuration.capabilityId); + if (s_deprecatedHashedCapabilityIds.contains(configuration.capabilityId)) + revert CapabilityIsDeprecated(configuration.capabilityId); - if (s_dons[id].capabilityIds.contains(capabilityId)) revert DuplicateDONCapability(id, capabilityId); + if (donCapabilityConfig.capabilityConfigs[configuration.capabilityId].length > 0) + revert DuplicateDONCapability(donId, configuration.capabilityId); for (uint256 j; j < nodes.length; ++j) { - bytes32 nodeP2PId = nodes[j]; - if (!s_nodes[nodeP2PId].supportedCapabilityIds[s_nodes[nodeP2PId].configCount].contains(capabilityId)) - revert NodeDoesNotSupportCapability(nodeP2PId, capabilityId); + if ( + !s_nodes[nodes[j]].supportedCapabilityIds[s_nodes[nodes[j]].configCount].contains(configuration.capabilityId) + ) revert NodeDoesNotSupportCapability(nodes[j], configuration.capabilityId); } - s_dons[id].capabilityIds.add(capabilityId); - s_dons[id].capabilityConfigs[capabilityId] = configuration.config; + donCapabilityConfig.capabilityIds.push(configuration.capabilityId); + donCapabilityConfig.capabilityConfigs[configuration.capabilityId] = configuration.config; + + _setDONCapabilityConfig(donId, configCount, configuration.capabilityId, nodes, configuration.config); } + s_dons[donId].isPublic = isPublic; + s_dons[donId].configCount = configCount; + emit ConfigSet(donId, configCount); + } - ++s_donId; + /// @notice Sets the capability's config on the config contract + /// @param donId The ID of the DON the capability is being configured for + /// @param configCount The number of times the DON has been configured + /// @param capabilityId The capability's ID + /// @param nodes The nodes in the DON + /// @param config The DON's capability config + /// @dev Helper function used to resolve stack too deep errors in _setDONConfig + function _setDONCapabilityConfig( + uint32 donId, + uint32 configCount, + bytes32 capabilityId, + bytes32[] calldata nodes, + bytes memory config + ) internal { + if (s_capabilities[capabilityId].configurationContract != address(0)) { + ICapabilityConfiguration(s_capabilities[capabilityId].configurationContract).beforeCapabilityConfigSet( + nodes, + config, + configCount, + donId + ); + } + } - emit DONAdded(id, isPublic); + /// @notice Gets a node's data + /// @param p2pId The P2P ID of the node to query for + /// @return NodeInfo The node data + /// @return configCount The number of times the node has been configured + function _getNode(bytes32 p2pId) internal view returns (NodeInfo memory, uint32 configCount) { + return ( + NodeInfo({ + nodeOperatorId: s_nodes[p2pId].nodeOperatorId, + p2pId: s_nodes[p2pId].p2pId, + signer: s_nodes[p2pId].signer, + hashedCapabilityIds: s_nodes[p2pId].supportedCapabilityIds[s_nodes[p2pId].configCount].values() + }), + s_nodes[p2pId].configCount + ); } /// @notice Gets DON's data /// @param donId The DON ID - /// @return uint32 The DON ID - /// @return bool True if the DON is public - /// @return bytes32[] The list of node P2P IDs that are in the DON - /// @return CapabilityConfiguration[] The list of capability configurations supported by the DON - function getDON( - uint32 donId - ) external view returns (uint32, bool, bytes32[] memory, CapabilityConfiguration[] memory) { - bytes32[] memory capabilityIds = s_dons[donId].capabilityIds.values(); + /// @return DONInfo The DON's parameters + function _getDON(uint32 donId) internal view returns (DONInfo memory) { + uint32 configCount = s_dons[donId].configCount; + + DONCapabilityConfig storage donCapabilityConfig = s_dons[donId].config[configCount]; + + bytes32[] memory capabilityIds = donCapabilityConfig.capabilityIds; CapabilityConfiguration[] memory capabilityConfigurations = new CapabilityConfiguration[](capabilityIds.length); for (uint256 i; i < capabilityConfigurations.length; ++i) { capabilityConfigurations[i] = CapabilityConfiguration({ capabilityId: capabilityIds[i], - config: s_dons[donId].capabilityConfigs[capabilityIds[i]] + config: donCapabilityConfig.capabilityConfigs[capabilityIds[i]] }); } - return (s_dons[donId].id, s_dons[donId].isPublic, s_dons[donId].nodes.values(), capabilityConfigurations); - } - - /// @notice Returns the DON specific configuration for a capability - /// @param donId The DON's ID - /// @param capabilityId The Capability ID - /// @return bytes The DON specific configuration for the capability - function getDONCapabilityConfig(uint32 donId, bytes32 capabilityId) external view returns (bytes memory) { - return s_dons[donId].capabilityConfigs[capabilityId]; + return + DONInfo({ + id: s_dons[donId].id, + configCount: configCount, + isPublic: s_dons[donId].isPublic, + nodeP2PIds: donCapabilityConfig.nodes.values(), + capabilityConfigurations: capabilityConfigurations + }); } } diff --git a/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol b/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol index 20447c9680a..4eb2c8197e3 100644 --- a/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol +++ b/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol @@ -20,8 +20,15 @@ interface ICapabilityConfiguration { /// @return configuration DON's configuration for the capability. function getCapabilityConfiguration(uint256 donId) external view returns (bytes memory configuration); - // Solidity does not support generic returns types, so this cannot be part of - // the interface. However, the implementation contract MAY implement this - // function to enable configuration decoding on-chain. - // function decodeCapabilityConfiguration(bytes configuration) external returns (TypedCapabilityConfigStruct config) + /// @notice Called by the registry prior to the config being set for a particular DON. + /// @param nodes The nodes that the configuration is being set for. + /// @param donCapabilityConfig The configuration being set on the capability registry. + /// @param donCapabilityConfigCount The number of times the DON has been configured, tracked on the capability registry. + /// @param donId The DON ID on the capability registry. + function beforeCapabilityConfigSet( + bytes32[] calldata nodes, + bytes calldata donCapabilityConfig, + uint64 donCapabilityConfigCount, + uint32 donId + ) external; } diff --git a/contracts/src/v0.8/keystone/test/CapabiityRegistry_GetHashedCapabilityIdTest.t.sol b/contracts/src/v0.8/keystone/test/CapabiityRegistry_GetHashedCapabilityIdTest.t.sol new file mode 100644 index 00000000000..9f8c0644e89 --- /dev/null +++ b/contracts/src/v0.8/keystone/test/CapabiityRegistry_GetHashedCapabilityIdTest.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {BaseTest} from "./BaseTest.t.sol"; +import {CapabilityConfigurationContract} from "./mocks/CapabilityConfigurationContract.sol"; + +import {CapabilityRegistry} from "../CapabilityRegistry.sol"; + +contract CapabilityRegistry_GetHashedCapabilityTest is BaseTest { + bytes32 constant CAPABILITY_LABELLED_NAME = bytes32("ccip1"); + bytes32 constant CAPABILITY_VERSION = bytes32("1.0.0"); + + function test_CorrectlyGeneratesHashedCapabilityId() public { + bytes32 expectedHashedCapabilityId = keccak256(abi.encode(CAPABILITY_LABELLED_NAME, CAPABILITY_VERSION)); + + assertEq( + s_capabilityRegistry.getHashedCapabilityId(CAPABILITY_LABELLED_NAME, CAPABILITY_VERSION), + expectedHashedCapabilityId + ); + } + + function test_DoesNotCauseIncorrectClashes() public { + assertNotEq( + s_capabilityRegistry.getHashedCapabilityId(CAPABILITY_LABELLED_NAME, CAPABILITY_VERSION), + s_capabilityRegistry.getHashedCapabilityId(bytes32("ccip"), bytes32("11.0.0")) + ); + } +} diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddDONTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddDONTest.t.sol index 9b215edccdc..259332f0de7 100644 --- a/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddDONTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddDONTest.t.sol @@ -2,16 +2,18 @@ pragma solidity ^0.8.19; import {BaseTest} from "./BaseTest.t.sol"; - +import {ICapabilityConfiguration} from "../interfaces/ICapabilityConfiguration.sol"; import {CapabilityRegistry} from "../CapabilityRegistry.sol"; contract CapabilityRegistry_AddDONTest is BaseTest { - event DONAdded(uint256 donId, bool isPublic); + event ConfigSet(uint32 donId, uint32 configCount); - uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 0; - uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 1; + uint32 private constant DON_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; bytes32 private constant INVALID_P2P_ID = bytes32("fake-p2p"); - bytes private constant CONFIG = bytes("onchain-config"); + bytes private constant BASIC_CAPABILITY_CONFIG = bytes("basic-capability-config"); + bytes private constant CONFIG_CAPABILITY_CONFIG = bytes("config-capability-config"); function setUp() public override { BaseTest.setUp(); @@ -20,12 +22,12 @@ contract CapabilityRegistry_AddDONTest is BaseTest { s_capabilityRegistry.addCapability(s_basicCapability); s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](2); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](2); bytes32[] memory capabilityIds = new bytes32[](2); capabilityIds[0] = s_basicHashedCapabilityId; capabilityIds[1] = s_capabilityWithConfigurationContractId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -35,7 +37,7 @@ contract CapabilityRegistry_AddDONTest is BaseTest { bytes32[] memory nodeTwoCapabilityIds = new bytes32[](1); nodeTwoCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[1] = CapabilityRegistry.NodeParams({ + nodes[1] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID_TWO, signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, @@ -57,7 +59,7 @@ contract CapabilityRegistry_AddDONTest is BaseTest { capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ capabilityId: s_basicHashedCapabilityId, - config: CONFIG + config: BASIC_CAPABILITY_CONFIG }); s_capabilityRegistry.addDON(nodes, capabilityConfigs, true); } @@ -69,7 +71,7 @@ contract CapabilityRegistry_AddDONTest is BaseTest { memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ capabilityId: s_capabilityWithConfigurationContractId, - config: CONFIG + config: CONFIG_CAPABILITY_CONFIG }); vm.expectRevert( abi.encodeWithSelector( @@ -87,7 +89,7 @@ contract CapabilityRegistry_AddDONTest is BaseTest { memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ capabilityId: s_nonExistentHashedCapabilityId, - config: CONFIG + config: BASIC_CAPABILITY_CONFIG }); vm.expectRevert( abi.encodeWithSelector(CapabilityRegistry.CapabilityDoesNotExist.selector, s_nonExistentHashedCapabilityId) @@ -103,11 +105,11 @@ contract CapabilityRegistry_AddDONTest is BaseTest { memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](2); capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ capabilityId: s_basicHashedCapabilityId, - config: CONFIG + config: BASIC_CAPABILITY_CONFIG }); capabilityConfigs[1] = CapabilityRegistry.CapabilityConfiguration({ capabilityId: s_basicHashedCapabilityId, - config: CONFIG + config: BASIC_CAPABILITY_CONFIG }); vm.expectRevert( @@ -125,7 +127,10 @@ contract CapabilityRegistry_AddDONTest is BaseTest { CapabilityRegistry.CapabilityConfiguration[] memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); - capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({capabilityId: capabilityId, config: CONFIG}); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: capabilityId, + config: BASIC_CAPABILITY_CONFIG + }); vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.CapabilityIsDeprecated.selector, capabilityId)); s_capabilityRegistry.addDON(nodes, capabilityConfigs, true); @@ -140,7 +145,7 @@ contract CapabilityRegistry_AddDONTest is BaseTest { memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ capabilityId: s_basicHashedCapabilityId, - config: CONFIG + config: BASIC_CAPABILITY_CONFIG }); vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.DuplicateDONNode.selector, 1, P2P_ID)); s_capabilityRegistry.addDON(nodes, capabilityConfigs, true); @@ -151,29 +156,40 @@ contract CapabilityRegistry_AddDONTest is BaseTest { nodes[0] = P2P_ID; CapabilityRegistry.CapabilityConfiguration[] - memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](2); capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ capabilityId: s_basicHashedCapabilityId, - config: CONFIG + config: BASIC_CAPABILITY_CONFIG + }); + capabilityConfigs[1] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_capabilityWithConfigurationContractId, + config: CONFIG_CAPABILITY_CONFIG }); vm.expectEmit(true, true, true, true, address(s_capabilityRegistry)); - emit DONAdded(1, true); + emit ConfigSet(DON_ID, 1); + vm.expectCall( + address(s_capabilityConfigurationContract), + abi.encodeWithSelector( + ICapabilityConfiguration.beforeCapabilityConfigSet.selector, + nodes, + CONFIG_CAPABILITY_CONFIG, + 1, + DON_ID + ), + 1 + ); s_capabilityRegistry.addDON(nodes, capabilityConfigs, true); - ( - uint32 id, - bool isPublic, - bytes32[] memory donNodes, - CapabilityRegistry.CapabilityConfiguration[] memory donCapabilityConfigs - ) = s_capabilityRegistry.getDON(1); - assertEq(id, 1); - assertEq(isPublic, true); - assertEq(donCapabilityConfigs.length, 1); - assertEq(donCapabilityConfigs[0].capabilityId, s_basicHashedCapabilityId); - assertEq(s_capabilityRegistry.getDONCapabilityConfig(1, s_basicHashedCapabilityId), CONFIG); - - assertEq(donNodes.length, 1); - assertEq(donNodes[0], P2P_ID); + CapabilityRegistry.DONInfo memory donInfo = s_capabilityRegistry.getDON(DON_ID); + assertEq(donInfo.id, DON_ID); + assertEq(donInfo.configCount, 1); + assertEq(donInfo.isPublic, true); + assertEq(donInfo.capabilityConfigurations.length, capabilityConfigs.length); + assertEq(donInfo.capabilityConfigurations[0].capabilityId, s_basicHashedCapabilityId); + assertEq(s_capabilityRegistry.getDONCapabilityConfig(DON_ID, s_basicHashedCapabilityId), BASIC_CAPABILITY_CONFIG); + + assertEq(donInfo.nodeP2PIds.length, nodes.length); + assertEq(donInfo.nodeP2PIds[0], P2P_ID); } } diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddNodeOperatorsTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddNodeOperatorsTest.t.sol index 388c3ca9cdd..b2f2d7bef38 100644 --- a/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddNodeOperatorsTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddNodeOperatorsTest.t.sol @@ -7,6 +7,9 @@ import {CapabilityRegistry} from "../CapabilityRegistry.sol"; contract CapabilityRegistry_AddNodeOperatorsTest is BaseTest { event NodeOperatorAdded(uint256 nodeOperatorId, address indexed admin, string name); + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; + function test_RevertWhen_CalledByNonAdmin() public { changePrank(STRANGER); vm.expectRevert("Only callable by owner"); @@ -25,16 +28,20 @@ contract CapabilityRegistry_AddNodeOperatorsTest is BaseTest { changePrank(ADMIN); vm.expectEmit(true, true, true, true, address(s_capabilityRegistry)); - emit NodeOperatorAdded(0, NODE_OPERATOR_ONE_ADMIN, NODE_OPERATOR_ONE_NAME); + emit NodeOperatorAdded(TEST_NODE_OPERATOR_ONE_ID, NODE_OPERATOR_ONE_ADMIN, NODE_OPERATOR_ONE_NAME); vm.expectEmit(true, true, true, true, address(s_capabilityRegistry)); - emit NodeOperatorAdded(1, NODE_OPERATOR_TWO_ADMIN, NODE_OPERATOR_TWO_NAME); + emit NodeOperatorAdded(TEST_NODE_OPERATOR_TWO_ID, NODE_OPERATOR_TWO_ADMIN, NODE_OPERATOR_TWO_NAME); s_capabilityRegistry.addNodeOperators(_getNodeOperators()); - CapabilityRegistry.NodeOperator memory nodeOperatorOne = s_capabilityRegistry.getNodeOperator(0); + CapabilityRegistry.NodeOperator memory nodeOperatorOne = s_capabilityRegistry.getNodeOperator( + TEST_NODE_OPERATOR_ONE_ID + ); assertEq(nodeOperatorOne.admin, NODE_OPERATOR_ONE_ADMIN); assertEq(nodeOperatorOne.name, NODE_OPERATOR_ONE_NAME); - CapabilityRegistry.NodeOperator memory nodeOperatorTwo = s_capabilityRegistry.getNodeOperator(1); + CapabilityRegistry.NodeOperator memory nodeOperatorTwo = s_capabilityRegistry.getNodeOperator( + TEST_NODE_OPERATOR_TWO_ID + ); assertEq(nodeOperatorTwo.admin, NODE_OPERATOR_TWO_ADMIN); assertEq(nodeOperatorTwo.name, NODE_OPERATOR_TWO_NAME); } diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddNodesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddNodesTest.t.sol index 116f97e441e..d5253e1cce4 100644 --- a/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddNodesTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_AddNodesTest.t.sol @@ -5,10 +5,10 @@ import {BaseTest} from "./BaseTest.t.sol"; import {CapabilityRegistry} from "../CapabilityRegistry.sol"; contract CapabilityRegistry_AddNodesTest is BaseTest { - event NodeAdded(bytes32 p2pId, uint256 nodeOperatorId); + event NodeAdded(bytes32 p2pId, uint256 nodeOperatorId, bytes32 signer); - uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 0; - uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_TWO_ID = 2; function setUp() public override { BaseTest.setUp(); @@ -20,12 +20,12 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { function test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() public { changePrank(STRANGER); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -38,15 +38,15 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { function test_RevertWhen_SignerAddressEmpty() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, - signer: address(0), + signer: bytes32(""), hashedCapabilityIds: hashedCapabilityIds }); @@ -54,14 +54,43 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { s_capabilityRegistry.addNodes(nodes); } + function test_RevertWhen_SignerAddressNotUnique() public { + changePrank(NODE_OPERATOR_ONE_ADMIN); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); + + bytes32[] memory hashedCapabilityIds = new bytes32[](1); + hashedCapabilityIds[0] = s_basicHashedCapabilityId; + + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + + s_capabilityRegistry.addNodes(nodes); + + changePrank(NODE_OPERATOR_TWO_ADMIN); + + // Try adding another node with the same signer address + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_TWO_ID, + p2pId: P2P_ID_TWO, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.InvalidNodeSigner.selector)); + s_capabilityRegistry.addNodes(nodes); + } + function test_RevertWhen_AddingDuplicateP2PId() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -76,12 +105,12 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { function test_RevertWhen_P2PIDEmpty() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: bytes32(""), signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -94,11 +123,11 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { function test_RevertWhen_AddingNodeWithoutCapabilities() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](0); - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -111,12 +140,12 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { function test_RevertWhen_AddingNodeWithInvalidCapability() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_nonExistentHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -127,15 +156,15 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { s_capabilityRegistry.addNodes(nodes); } - function test_AddsNodeParams() public { + function test_AddsNodeInfo() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](2); hashedCapabilityIds[0] = s_basicHashedCapabilityId; hashedCapabilityIds[1] = s_capabilityWithConfigurationContractId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -143,10 +172,10 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { }); vm.expectEmit(address(s_capabilityRegistry)); - emit NodeAdded(P2P_ID, TEST_NODE_OPERATOR_ONE_ID); + emit NodeAdded(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NODE_OPERATOR_ONE_SIGNER_ADDRESS); s_capabilityRegistry.addNodes(nodes); - (CapabilityRegistry.NodeParams memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); + (CapabilityRegistry.NodeInfo memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); assertEq(node.p2pId, P2P_ID); assertEq(node.hashedCapabilityIds.length, 2); @@ -158,12 +187,12 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { function test_OwnerCanAddNodes() public { changePrank(ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](2); hashedCapabilityIds[0] = s_basicHashedCapabilityId; hashedCapabilityIds[1] = s_capabilityWithConfigurationContractId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -171,10 +200,10 @@ contract CapabilityRegistry_AddNodesTest is BaseTest { }); vm.expectEmit(address(s_capabilityRegistry)); - emit NodeAdded(P2P_ID, TEST_NODE_OPERATOR_ONE_ID); + emit NodeAdded(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NODE_OPERATOR_ONE_SIGNER_ADDRESS); s_capabilityRegistry.addNodes(nodes); - (CapabilityRegistry.NodeParams memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); + (CapabilityRegistry.NodeInfo memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); assertEq(node.p2pId, P2P_ID); assertEq(node.hashedCapabilityIds.length, 2); diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetDONsTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetDONsTest.t.sol new file mode 100644 index 00000000000..ccaa7076a4c --- /dev/null +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetDONsTest.t.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {BaseTest} from "./BaseTest.t.sol"; + +import {CapabilityRegistry} from "../CapabilityRegistry.sol"; + +contract CapabilityRegistry_GetDONsTest is BaseTest { + event ConfigSet(uint32 donId, uint32 configCount); + + uint32 private constant DON_ID_ONE = 1; + uint32 private constant DON_ID_TWO = 2; + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; + bytes32 private constant INVALID_P2P_ID = bytes32("fake-p2p"); + bytes private constant CONFIG = bytes("onchain-config"); + CapabilityRegistry.CapabilityConfiguration[] private s_capabilityConfigs; + + function setUp() public override { + BaseTest.setUp(); + + s_capabilityRegistry.addNodeOperators(_getNodeOperators()); + s_capabilityRegistry.addCapability(s_basicCapability); + s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract); + + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](2); + bytes32[] memory capabilityIds = new bytes32[](2); + capabilityIds[0] = s_basicHashedCapabilityId; + capabilityIds[1] = s_capabilityWithConfigurationContractId; + + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: capabilityIds + }); + + bytes32[] memory nodeTwoCapabilityIds = new bytes32[](1); + nodeTwoCapabilityIds[0] = s_basicHashedCapabilityId; + + nodes[1] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID_TWO, + signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + hashedCapabilityIds: nodeTwoCapabilityIds + }); + + changePrank(NODE_OPERATOR_ONE_ADMIN); + s_capabilityRegistry.addNodes(nodes); + + s_capabilityConfigs.push( + CapabilityRegistry.CapabilityConfiguration({capabilityId: s_basicHashedCapabilityId, config: CONFIG}) + ); + + bytes32[] memory nodeIds = new bytes32[](2); + nodeIds[0] = P2P_ID; + nodeIds[1] = P2P_ID_TWO; + + changePrank(ADMIN); + s_capabilityRegistry.addDON(nodeIds, s_capabilityConfigs, true); + s_capabilityRegistry.addDON(nodeIds, s_capabilityConfigs, false); + } + + function test_CorrectlyFetchesDONs() public view { + CapabilityRegistry.DONInfo[] memory dons = s_capabilityRegistry.getDONs(); + assertEq(dons.length, 2); + assertEq(dons[0].id, DON_ID_ONE); + assertEq(dons[0].configCount, 1); + assertEq(dons[0].isPublic, true); + assertEq(dons[0].capabilityConfigurations.length, s_capabilityConfigs.length); + assertEq(dons[0].capabilityConfigurations[0].capabilityId, s_basicHashedCapabilityId); + + assertEq(dons[1].id, DON_ID_TWO); + assertEq(dons[1].configCount, 1); + assertEq(dons[1].isPublic, false); + assertEq(dons[1].capabilityConfigurations.length, s_capabilityConfigs.length); + assertEq(dons[1].capabilityConfigurations[0].capabilityId, s_basicHashedCapabilityId); + } + + function test_DoesNotIncludeRemovedDONs() public { + uint32[] memory removedDONIDs = new uint32[](1); + removedDONIDs[0] = DON_ID_ONE; + s_capabilityRegistry.removeDONs(removedDONIDs); + + CapabilityRegistry.DONInfo[] memory dons = s_capabilityRegistry.getDONs(); + assertEq(dons.length, 1); + assertEq(dons[0].id, DON_ID_TWO); + assertEq(dons[0].configCount, 1); + assertEq(dons[0].isPublic, false); + assertEq(dons[0].capabilityConfigurations.length, s_capabilityConfigs.length); + assertEq(dons[0].capabilityConfigurations[0].capabilityId, s_basicHashedCapabilityId); + } +} diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetNodeOperatorsTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetNodeOperatorsTest.t.sol new file mode 100644 index 00000000000..95d3084caae --- /dev/null +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetNodeOperatorsTest.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {BaseTest} from "./BaseTest.t.sol"; +import {CapabilityRegistry} from "../CapabilityRegistry.sol"; + +contract CapabilityRegistry_GetNodeOperatorsTest is BaseTest { + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; + + function setUp() public override { + BaseTest.setUp(); + changePrank(ADMIN); + s_capabilityRegistry.addNodeOperators(_getNodeOperators()); + } + + function test_CorrectlyFetchesNodeOperators() public view { + CapabilityRegistry.NodeOperator[] memory nodeOperators = s_capabilityRegistry.getNodeOperators(); + assertEq(nodeOperators.length, 2); + + assertEq(nodeOperators[0].admin, NODE_OPERATOR_ONE_ADMIN); + assertEq(nodeOperators[0].name, NODE_OPERATOR_ONE_NAME); + + assertEq(nodeOperators[1].admin, NODE_OPERATOR_TWO_ADMIN); + assertEq(nodeOperators[1].name, NODE_OPERATOR_TWO_NAME); + } + + function test_DoesNotIncludeRemovedNodeOperators() public { + changePrank(ADMIN); + uint256[] memory nodeOperatorsToRemove = new uint256[](1); + nodeOperatorsToRemove[0] = 2; + s_capabilityRegistry.removeNodeOperators(nodeOperatorsToRemove); + + CapabilityRegistry.NodeOperator[] memory nodeOperators = s_capabilityRegistry.getNodeOperators(); + assertEq(nodeOperators.length, 1); + + assertEq(nodeOperators[0].admin, NODE_OPERATOR_ONE_ADMIN); + assertEq(nodeOperators[0].name, NODE_OPERATOR_ONE_NAME); + } +} diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetNodesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetNodesTest.t.sol new file mode 100644 index 00000000000..02dace7d0e9 --- /dev/null +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_GetNodesTest.t.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {BaseTest} from "./BaseTest.t.sol"; +import {CapabilityRegistry} from "../CapabilityRegistry.sol"; + +contract CapabilityRegistry_GetNodesTest is BaseTest { + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; + + function setUp() public override { + BaseTest.setUp(); + changePrank(ADMIN); + s_capabilityRegistry.addNodeOperators(_getNodeOperators()); + s_capabilityRegistry.addCapability(s_basicCapability); + s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract); + + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](2); + bytes32[] memory hashedCapabilityIds = new bytes32[](2); + hashedCapabilityIds[0] = s_basicHashedCapabilityId; + hashedCapabilityIds[1] = s_capabilityWithConfigurationContractId; + + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + + nodes[1] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID_TWO, + signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + + changePrank(NODE_OPERATOR_ONE_ADMIN); + + s_capabilityRegistry.addNodes(nodes); + } + + function test_CorrectlyFetchesNodes() public view { + (CapabilityRegistry.NodeInfo[] memory nodes, uint32[] memory configCounts) = s_capabilityRegistry.getNodes(); + assertEq(nodes.length, 2); + + assertEq(nodes[0].nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); + assertEq(nodes[0].signer, NODE_OPERATOR_ONE_SIGNER_ADDRESS); + assertEq(nodes[0].p2pId, P2P_ID); + assertEq(nodes[0].hashedCapabilityIds.length, 2); + assertEq(nodes[0].hashedCapabilityIds[0], s_basicHashedCapabilityId); + assertEq(nodes[0].hashedCapabilityIds[1], s_capabilityWithConfigurationContractId); + assertEq(configCounts[0], 1); + + assertEq(nodes[1].nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); + assertEq(nodes[1].signer, NODE_OPERATOR_TWO_SIGNER_ADDRESS); + assertEq(nodes[1].p2pId, P2P_ID_TWO); + assertEq(nodes[1].hashedCapabilityIds.length, 2); + assertEq(nodes[1].hashedCapabilityIds[0], s_basicHashedCapabilityId); + assertEq(nodes[1].hashedCapabilityIds[1], s_capabilityWithConfigurationContractId); + assertEq(configCounts[1], 1); + } + + function test_DoesNotIncludeRemovedNodes() public { + changePrank(ADMIN); + bytes32[] memory nodesToRemove = new bytes32[](1); + nodesToRemove[0] = P2P_ID_TWO; + s_capabilityRegistry.removeNodes(nodesToRemove); + + (CapabilityRegistry.NodeInfo[] memory nodes, uint32[] memory configCounts) = s_capabilityRegistry.getNodes(); + assertEq(nodes.length, 1); + + assertEq(nodes[0].nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); + assertEq(nodes[0].signer, NODE_OPERATOR_ONE_SIGNER_ADDRESS); + assertEq(nodes[0].p2pId, P2P_ID); + assertEq(nodes[0].hashedCapabilityIds.length, 2); + assertEq(nodes[0].hashedCapabilityIds[0], s_basicHashedCapabilityId); + assertEq(nodes[0].hashedCapabilityIds[1], s_capabilityWithConfigurationContractId); + assertEq(configCounts[0], 1); + } +} diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveDONsTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveDONsTest.t.sol new file mode 100644 index 00000000000..53f602c9da2 --- /dev/null +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveDONsTest.t.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {BaseTest} from "./BaseTest.t.sol"; + +import {CapabilityRegistry} from "../CapabilityRegistry.sol"; + +contract CapabilityRegistry_RemoveDONsTest is BaseTest { + event ConfigSet(uint32 donId, uint32 configCount); + + uint32 private constant DON_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; + bytes32 private constant INVALID_P2P_ID = bytes32("fake-p2p"); + bytes private constant CONFIG = bytes("onchain-config"); + + function setUp() public override { + BaseTest.setUp(); + + s_capabilityRegistry.addNodeOperators(_getNodeOperators()); + s_capabilityRegistry.addCapability(s_basicCapability); + s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract); + + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](2); + bytes32[] memory capabilityIds = new bytes32[](2); + capabilityIds[0] = s_basicHashedCapabilityId; + capabilityIds[1] = s_capabilityWithConfigurationContractId; + + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: capabilityIds + }); + + bytes32[] memory nodeTwoCapabilityIds = new bytes32[](1); + nodeTwoCapabilityIds[0] = s_basicHashedCapabilityId; + + nodes[1] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID_TWO, + signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + hashedCapabilityIds: nodeTwoCapabilityIds + }); + + changePrank(NODE_OPERATOR_ONE_ADMIN); + s_capabilityRegistry.addNodes(nodes); + + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: CONFIG + }); + + bytes32[] memory nodeIds = new bytes32[](2); + nodeIds[0] = P2P_ID; + nodeIds[1] = P2P_ID_TWO; + + changePrank(ADMIN); + s_capabilityRegistry.addDON(nodeIds, capabilityConfigs, true); + } + + function test_RevertWhen_CalledByNonAdmin() public { + uint32[] memory donIDs = new uint32[](1); + donIDs[0] = 1; + changePrank(STRANGER); + vm.expectRevert("Only callable by owner"); + s_capabilityRegistry.removeDONs(donIDs); + } + + function test_RevertWhen_DONDoesNotExist() public { + uint32 invalidDONId = 10; + uint32[] memory donIDs = new uint32[](1); + donIDs[0] = invalidDONId; + vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.DONDoesNotExist.selector, invalidDONId)); + s_capabilityRegistry.removeDONs(donIDs); + } + + function test_RemovesDON() public { + uint32[] memory donIDs = new uint32[](1); + donIDs[0] = DON_ID; + vm.expectEmit(true, true, true, true, address(s_capabilityRegistry)); + emit ConfigSet(DON_ID, 0); + s_capabilityRegistry.removeDONs(donIDs); + + CapabilityRegistry.DONInfo memory donInfo = s_capabilityRegistry.getDON(DON_ID); + assertEq(donInfo.id, 0); + assertEq(donInfo.configCount, 0); + assertEq(donInfo.isPublic, false); + assertEq(donInfo.capabilityConfigurations.length, 0); + assertEq(s_capabilityRegistry.getDONCapabilityConfig(DON_ID, s_basicHashedCapabilityId), bytes("")); + assertEq(donInfo.nodeP2PIds.length, 0); + } +} diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveNodeOperatorsTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveNodeOperatorsTest.t.sol index 18b0ed6e2ac..99106692c3b 100644 --- a/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveNodeOperatorsTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveNodeOperatorsTest.t.sol @@ -7,8 +7,8 @@ import {CapabilityRegistry} from "../CapabilityRegistry.sol"; contract CapabilityRegistry_RemoveNodeOperatorsTest is BaseTest { event NodeOperatorRemoved(uint256 nodeOperatorId); - uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 0; - uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; function setUp() public override { BaseTest.setUp(); @@ -32,14 +32,19 @@ contract CapabilityRegistry_RemoveNodeOperatorsTest is BaseTest { vm.expectEmit(true, true, true, true, address(s_capabilityRegistry)); emit NodeOperatorRemoved(TEST_NODE_OPERATOR_TWO_ID); uint256[] memory nodeOperatorsToRemove = new uint256[](2); - nodeOperatorsToRemove[1] = 1; + nodeOperatorsToRemove[0] = TEST_NODE_OPERATOR_ONE_ID; + nodeOperatorsToRemove[1] = TEST_NODE_OPERATOR_TWO_ID; s_capabilityRegistry.removeNodeOperators(nodeOperatorsToRemove); - CapabilityRegistry.NodeOperator memory nodeOperatorOne = s_capabilityRegistry.getNodeOperator(0); + CapabilityRegistry.NodeOperator memory nodeOperatorOne = s_capabilityRegistry.getNodeOperator( + TEST_NODE_OPERATOR_ONE_ID + ); assertEq(nodeOperatorOne.admin, address(0)); assertEq(nodeOperatorOne.name, ""); - CapabilityRegistry.NodeOperator memory nodeOperatorTwo = s_capabilityRegistry.getNodeOperator(1); + CapabilityRegistry.NodeOperator memory nodeOperatorTwo = s_capabilityRegistry.getNodeOperator( + TEST_NODE_OPERATOR_TWO_ID + ); assertEq(nodeOperatorTwo.admin, address(0)); assertEq(nodeOperatorTwo.name, ""); } diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveNodesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveNodesTest.t.sol index 8e3542c8825..d0feff53dac 100644 --- a/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveNodesTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_RemoveNodesTest.t.sol @@ -7,8 +7,8 @@ import {CapabilityRegistry} from "../CapabilityRegistry.sol"; contract CapabilityRegistry_RemoveNodesTest is BaseTest { event NodeRemoved(bytes32 p2pId); - uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 0; - uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; bytes32 private constant INVALID_P2P_ID = bytes32("fake-p2p"); function setUp() public override { @@ -18,12 +18,12 @@ contract CapabilityRegistry_RemoveNodesTest is BaseTest { s_capabilityRegistry.addCapability(s_basicCapability); s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](2); hashedCapabilityIds[0] = s_basicHashedCapabilityId; hashedCapabilityIds[1] = s_capabilityWithConfigurationContractId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -72,14 +72,45 @@ contract CapabilityRegistry_RemoveNodesTest is BaseTest { emit NodeRemoved(P2P_ID); s_capabilityRegistry.removeNodes(nodes); - (CapabilityRegistry.NodeParams memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); + (CapabilityRegistry.NodeInfo memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); assertEq(node.nodeOperatorId, 0); assertEq(node.p2pId, bytes32("")); - assertEq(node.signer, address(0)); + assertEq(node.signer, bytes32("")); assertEq(node.hashedCapabilityIds.length, 0); assertEq(configCount, 0); } + function test_CanAddNodeWithSameSignerAddressAfterRemoving() public { + changePrank(NODE_OPERATOR_ONE_ADMIN); + + bytes32[] memory nodes = new bytes32[](1); + nodes[0] = P2P_ID; + + s_capabilityRegistry.removeNodes(nodes); + + CapabilityRegistry.NodeInfo[] memory NodeInfo = new CapabilityRegistry.NodeInfo[](1); + bytes32[] memory hashedCapabilityIds = new bytes32[](2); + hashedCapabilityIds[0] = s_basicHashedCapabilityId; + hashedCapabilityIds[1] = s_capabilityWithConfigurationContractId; + + NodeInfo[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + + s_capabilityRegistry.addNodes(NodeInfo); + + (CapabilityRegistry.NodeInfo memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); + assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); + assertEq(node.p2pId, P2P_ID); + assertEq(node.hashedCapabilityIds.length, 2); + assertEq(node.hashedCapabilityIds[0], s_basicHashedCapabilityId); + assertEq(node.hashedCapabilityIds[1], s_capabilityWithConfigurationContractId); + assertEq(configCount, 1); + } + function test_OwnerCanRemoveNodes() public { changePrank(ADMIN); @@ -90,10 +121,10 @@ contract CapabilityRegistry_RemoveNodesTest is BaseTest { emit NodeRemoved(P2P_ID); s_capabilityRegistry.removeNodes(nodes); - (CapabilityRegistry.NodeParams memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); + (CapabilityRegistry.NodeInfo memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); assertEq(node.nodeOperatorId, 0); assertEq(node.p2pId, bytes32("")); - assertEq(node.signer, address(0)); + assertEq(node.signer, bytes32("")); assertEq(node.hashedCapabilityIds.length, 0); assertEq(configCount, 0); } diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_UpdateDONTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_UpdateDONTest.t.sol new file mode 100644 index 00000000000..6473839cd43 --- /dev/null +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_UpdateDONTest.t.sol @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {BaseTest} from "./BaseTest.t.sol"; +import {ICapabilityConfiguration} from "../interfaces/ICapabilityConfiguration.sol"; +import {CapabilityRegistry} from "../CapabilityRegistry.sol"; + +contract CapabilityRegistry_UpdateDONTest is BaseTest { + event ConfigSet(uint32 donId, uint32 configCount); + + uint32 private constant DON_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 2; + bytes32 private constant INVALID_P2P_ID = bytes32("fake-p2p"); + bytes private constant BASIC_CAPABILITY_CONFIG = bytes("basic-capability-config"); + bytes private constant CONFIG_CAPABILITY_CONFIG = bytes("config-capability-config"); + + function setUp() public override { + BaseTest.setUp(); + + s_capabilityRegistry.addNodeOperators(_getNodeOperators()); + s_capabilityRegistry.addCapability(s_basicCapability); + s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract); + + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](2); + bytes32[] memory capabilityIds = new bytes32[](2); + capabilityIds[0] = s_basicHashedCapabilityId; + capabilityIds[1] = s_capabilityWithConfigurationContractId; + + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: capabilityIds + }); + + bytes32[] memory nodeTwoCapabilityIds = new bytes32[](1); + nodeTwoCapabilityIds[0] = s_basicHashedCapabilityId; + + nodes[1] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID_TWO, + signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + hashedCapabilityIds: nodeTwoCapabilityIds + }); + + changePrank(NODE_OPERATOR_ONE_ADMIN); + s_capabilityRegistry.addNodes(nodes); + + changePrank(ADMIN); + + bytes32[] memory donNodes = new bytes32[](1); + donNodes[0] = P2P_ID; + + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + s_capabilityRegistry.addDON(donNodes, capabilityConfigs, true); + } + + function test_RevertWhen_CalledByNonAdmin() public { + changePrank(STRANGER); + vm.expectRevert("Only callable by owner"); + bytes32[] memory nodes = new bytes32[](1); + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + s_capabilityRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true); + } + + function test_RevertWhen_NodeDoesNotSupportCapability() public { + bytes32[] memory nodes = new bytes32[](1); + nodes[0] = P2P_ID_TWO; + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_capabilityWithConfigurationContractId, + config: CONFIG_CAPABILITY_CONFIG + }); + vm.expectRevert( + abi.encodeWithSelector( + CapabilityRegistry.NodeDoesNotSupportCapability.selector, + P2P_ID_TWO, + s_capabilityWithConfigurationContractId + ) + ); + s_capabilityRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true); + } + + function test_RevertWhen_DONDoesNotExist() public { + uint32 nonExistentDONId = 10; + bytes32[] memory nodes = new bytes32[](1); + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.DONDoesNotExist.selector, nonExistentDONId)); + s_capabilityRegistry.updateDON(nonExistentDONId, nodes, capabilityConfigs, true); + } + + function test_RevertWhen_CapabilityDoesNotExist() public { + bytes32[] memory nodes = new bytes32[](1); + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_nonExistentHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + vm.expectRevert( + abi.encodeWithSelector(CapabilityRegistry.CapabilityDoesNotExist.selector, s_nonExistentHashedCapabilityId) + ); + s_capabilityRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true); + } + + function test_RevertWhen_DuplicateCapabilityAdded() public { + bytes32[] memory nodes = new bytes32[](1); + nodes[0] = P2P_ID; + + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](2); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + capabilityConfigs[1] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + + vm.expectRevert( + abi.encodeWithSelector(CapabilityRegistry.DuplicateDONCapability.selector, 1, s_basicHashedCapabilityId) + ); + s_capabilityRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true); + } + + function test_RevertWhen_DeprecatedCapabilityAdded() public { + bytes32 capabilityId = s_basicHashedCapabilityId; + s_capabilityRegistry.deprecateCapability(capabilityId); + + bytes32[] memory nodes = new bytes32[](1); + nodes[0] = P2P_ID; + + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: capabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + + vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.CapabilityIsDeprecated.selector, capabilityId)); + s_capabilityRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true); + } + + function test_RevertWhen_DuplicateNodeAdded() public { + bytes32[] memory nodes = new bytes32[](2); + nodes[0] = P2P_ID; + nodes[1] = P2P_ID; + + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.DuplicateDONNode.selector, 1, P2P_ID)); + s_capabilityRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true); + } + + function test_UpdatesDON() public { + bytes32[] memory nodes = new bytes32[](1); + nodes[0] = P2P_ID; + + CapabilityRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](2); + capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + capabilityConfigs[1] = CapabilityRegistry.CapabilityConfiguration({ + capabilityId: s_capabilityWithConfigurationContractId, + config: CONFIG_CAPABILITY_CONFIG + }); + + CapabilityRegistry.DONInfo memory oldDONInfo = s_capabilityRegistry.getDON(DON_ID); + + bool expectedDONIsPublic = false; + uint32 expectedConfigCount = oldDONInfo.configCount + 1; + + vm.expectEmit(true, true, true, true, address(s_capabilityRegistry)); + emit ConfigSet(DON_ID, expectedConfigCount); + vm.expectCall( + address(s_capabilityConfigurationContract), + abi.encodeWithSelector( + ICapabilityConfiguration.beforeCapabilityConfigSet.selector, + nodes, + CONFIG_CAPABILITY_CONFIG, + expectedConfigCount, + DON_ID + ), + 1 + ); + s_capabilityRegistry.updateDON(DON_ID, nodes, capabilityConfigs, expectedDONIsPublic); + + CapabilityRegistry.DONInfo memory donInfo = s_capabilityRegistry.getDON(DON_ID); + assertEq(donInfo.id, DON_ID); + assertEq(donInfo.configCount, expectedConfigCount); + assertEq(donInfo.isPublic, false); + assertEq(donInfo.capabilityConfigurations.length, capabilityConfigs.length); + assertEq(donInfo.capabilityConfigurations[0].capabilityId, s_basicHashedCapabilityId); + assertEq(s_capabilityRegistry.getDONCapabilityConfig(DON_ID, s_basicHashedCapabilityId), BASIC_CAPABILITY_CONFIG); + + assertEq(donInfo.nodeP2PIds.length, nodes.length); + assertEq(donInfo.nodeP2PIds[0], P2P_ID); + } +} diff --git a/contracts/src/v0.8/keystone/test/CapabilityRegistry_UpdateNodesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilityRegistry_UpdateNodesTest.t.sol index e97fde01209..3b099606a70 100644 --- a/contracts/src/v0.8/keystone/test/CapabilityRegistry_UpdateNodesTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilityRegistry_UpdateNodesTest.t.sol @@ -5,11 +5,12 @@ import {BaseTest} from "./BaseTest.t.sol"; import {CapabilityRegistry} from "../CapabilityRegistry.sol"; contract CapabilityRegistry_UpdateNodesTest is BaseTest { - event NodeUpdated(bytes32 p2pId, uint256 nodeOperatorId, address signer); + event NodeUpdated(bytes32 p2pId, uint256 nodeOperatorId, bytes32 signer); - uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 0; - uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 1; + uint32 private constant TEST_NODE_OPERATOR_TWO_ID = 2; bytes32 private constant INVALID_P2P_ID = bytes32("fake-p2p"); + bytes32 private constant NEW_NODE_SIGNER = bytes32("new-signer"); function setUp() public override { BaseTest.setUp(); @@ -18,12 +19,12 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { s_capabilityRegistry.addCapability(s_basicCapability); s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](2); hashedCapabilityIds[0] = s_basicHashedCapabilityId; hashedCapabilityIds[1] = s_capabilityWithConfigurationContractId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -31,18 +32,27 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { }); changePrank(NODE_OPERATOR_ONE_ADMIN); + s_capabilityRegistry.addNodes(nodes); + + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_TWO_ID, + p2pId: P2P_ID_TWO, + signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + changePrank(NODE_OPERATOR_TWO_ADMIN); s_capabilityRegistry.addNodes(nodes); } function test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() public { changePrank(STRANGER); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, @@ -55,12 +65,12 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { function test_RevertWhen_NodeDoesNotExist() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: INVALID_P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -73,12 +83,12 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { function test_RevertWhen_P2PIDEmpty() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: bytes32(""), signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -91,15 +101,15 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { function test_RevertWhen_SignerAddressEmpty() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, - signer: address(0), + signer: bytes32(""), hashedCapabilityIds: hashedCapabilityIds }); @@ -107,13 +117,31 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { s_capabilityRegistry.updateNodes(nodes); } + function test_RevertWhen_NodeSignerAlreadyAssignedToAnotherNode() public { + changePrank(NODE_OPERATOR_ONE_ADMIN); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); + + bytes32[] memory hashedCapabilityIds = new bytes32[](1); + hashedCapabilityIds[0] = s_basicHashedCapabilityId; + + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + + vm.expectRevert(CapabilityRegistry.InvalidNodeSigner.selector); + s_capabilityRegistry.updateNodes(nodes); + } + function test_RevertWhen_UpdatingNodeWithoutCapabilities() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](0); - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -126,12 +154,12 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { function test_RevertWhen_AddingNodeWithInvalidCapability() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_nonExistentHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, @@ -142,28 +170,59 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { s_capabilityRegistry.updateNodes(nodes); } - function test_UpdatesNodeParams() public { + function test_CanUpdateParamsIfNodeSignerAddressNoLongerUsed() public { changePrank(NODE_OPERATOR_ONE_ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + // Set node one's signer to another address + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, - signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + signer: bytes32(abi.encodePacked(address(6666))), + hashedCapabilityIds: hashedCapabilityIds + }); + + s_capabilityRegistry.updateNodes(nodes); + + // Set node two's signer to node one's signer + changePrank(NODE_OPERATOR_TWO_ADMIN); + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_TWO_ID, + p2pId: P2P_ID_TWO, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + s_capabilityRegistry.updateNodes(nodes); + + (CapabilityRegistry.NodeInfo memory node, ) = s_capabilityRegistry.getNode(P2P_ID_TWO); + assertEq(node.signer, NODE_OPERATOR_ONE_SIGNER_ADDRESS); + } + + function test_UpdatesNodeInfo() public { + changePrank(NODE_OPERATOR_ONE_ADMIN); + + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); + bytes32[] memory hashedCapabilityIds = new bytes32[](1); + hashedCapabilityIds[0] = s_basicHashedCapabilityId; + + nodes[0] = CapabilityRegistry.NodeInfo({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NEW_NODE_SIGNER, hashedCapabilityIds: hashedCapabilityIds }); vm.expectEmit(address(s_capabilityRegistry)); - emit NodeUpdated(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NODE_OPERATOR_TWO_SIGNER_ADDRESS); + emit NodeUpdated(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NEW_NODE_SIGNER); s_capabilityRegistry.updateNodes(nodes); - (CapabilityRegistry.NodeParams memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); + (CapabilityRegistry.NodeInfo memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); assertEq(node.p2pId, P2P_ID); - assertEq(node.signer, NODE_OPERATOR_TWO_SIGNER_ADDRESS); + assertEq(node.signer, NEW_NODE_SIGNER); assertEq(node.hashedCapabilityIds.length, 1); assertEq(node.hashedCapabilityIds[0], s_basicHashedCapabilityId); assertEq(configCount, 2); @@ -172,25 +231,25 @@ contract CapabilityRegistry_UpdateNodesTest is BaseTest { function test_OwnerCanUpdateNodes() public { changePrank(ADMIN); - CapabilityRegistry.NodeParams[] memory nodes = new CapabilityRegistry.NodeParams[](1); + CapabilityRegistry.NodeInfo[] memory nodes = new CapabilityRegistry.NodeInfo[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; - nodes[0] = CapabilityRegistry.NodeParams({ + nodes[0] = CapabilityRegistry.NodeInfo({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, - signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + signer: NEW_NODE_SIGNER, hashedCapabilityIds: hashedCapabilityIds }); vm.expectEmit(address(s_capabilityRegistry)); - emit NodeUpdated(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NODE_OPERATOR_TWO_SIGNER_ADDRESS); + emit NodeUpdated(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NEW_NODE_SIGNER); s_capabilityRegistry.updateNodes(nodes); - (CapabilityRegistry.NodeParams memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); + (CapabilityRegistry.NodeInfo memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID); assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); assertEq(node.p2pId, P2P_ID); - assertEq(node.signer, NODE_OPERATOR_TWO_SIGNER_ADDRESS); + assertEq(node.signer, NEW_NODE_SIGNER); assertEq(node.hashedCapabilityIds.length, 1); assertEq(node.hashedCapabilityIds[0], s_basicHashedCapabilityId); assertEq(configCount, 2); diff --git a/contracts/src/v0.8/keystone/test/Constants.t.sol b/contracts/src/v0.8/keystone/test/Constants.t.sol index 48308c6fbf5..54cd58e8541 100644 --- a/contracts/src/v0.8/keystone/test/Constants.t.sol +++ b/contracts/src/v0.8/keystone/test/Constants.t.sol @@ -6,10 +6,10 @@ contract Constants { address internal STRANGER = address(2); address internal NODE_OPERATOR_ONE_ADMIN = address(3); string internal NODE_OPERATOR_ONE_NAME = "node-operator-one"; - address internal NODE_OPERATOR_ONE_SIGNER_ADDRESS = address(3333); + bytes32 internal NODE_OPERATOR_ONE_SIGNER_ADDRESS = bytes32(abi.encodePacked(address(3333))); address internal NODE_OPERATOR_TWO_ADMIN = address(4); string internal NODE_OPERATOR_TWO_NAME = "node-operator-two"; - address internal NODE_OPERATOR_TWO_SIGNER_ADDRESS = address(4444); + bytes32 internal NODE_OPERATOR_TWO_SIGNER_ADDRESS = bytes32(abi.encodePacked(address(4444))); bytes32 internal P2P_ID = hex"e42415859707d90ed4dc534ad730f187a17b0c368e1beec2e9b995587c4b0a05"; bytes32 internal P2P_ID_TWO = hex"f53415859707d90ed4dc534ad730f187a17b0c368e1beec2e9b995587c4b0a05"; diff --git a/contracts/src/v0.8/keystone/test/mocks/CapabilityConfigurationContract.sol b/contracts/src/v0.8/keystone/test/mocks/CapabilityConfigurationContract.sol index 6f3a28b7ca0..c30a81714d8 100644 --- a/contracts/src/v0.8/keystone/test/mocks/CapabilityConfigurationContract.sol +++ b/contracts/src/v0.8/keystone/test/mocks/CapabilityConfigurationContract.sol @@ -11,7 +11,14 @@ contract CapabilityConfigurationContract is ICapabilityConfiguration, ERC165 { return s_donConfiguration[donId]; } + function beforeCapabilityConfigSet( + bytes32[] calldata nodes, + bytes calldata config, + uint64 configCount, + uint32 donId + ) external {} + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { - return interfaceId == this.getCapabilityConfiguration.selector; + return interfaceId == this.getCapabilityConfiguration.selector ^ this.beforeCapabilityConfigSet.selector; } } diff --git a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go index 3357bf8f5fe..1725189ced6 100644 --- a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go +++ b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go @@ -42,21 +42,29 @@ type CapabilityRegistryCapabilityConfiguration struct { Config []byte } -type CapabilityRegistryNodeOperator struct { - Admin common.Address - Name string +type CapabilityRegistryDONInfo struct { + Id uint32 + ConfigCount uint32 + IsPublic bool + NodeP2PIds [][32]byte + CapabilityConfigurations []CapabilityRegistryCapabilityConfiguration } -type CapabilityRegistryNodeParams struct { +type CapabilityRegistryNodeInfo struct { NodeOperatorId uint32 - Signer common.Address + Signer [32]byte P2pId [32]byte HashedCapabilityIds [][32]byte } +type CapabilityRegistryNodeOperator struct { + Admin common.Address + Name string +} + var CapabilityRegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"donId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"DONAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052600b805463ffffffff191660011790553480156200002157600080fd5b503380600081620000795760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000ac57620000ac81620000b5565b50505062000160565b336001600160a01b038216036200010f5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000070565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6134b380620001706000396000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806350e03b16116100d85780638da5cb5b1161008c578063b06e07a711610066578063b06e07a71461038f578063ddbe4f82146103a2578063f2fde38b146103b757600080fd5b80638da5cb5b146103345780639cb7c5f41461035c578063ae3c241c1461037c57600080fd5b806365c14dc7116100bd57806365c14dc7146102f95780636ae5c5911461031957806379ba50971461032c57600080fd5b806350e03b16146102d35780635840cd45146102e657600080fd5b8063235374051161012f57806336b402fb1161011457806336b402fb14610257578063398f37731461029f57806350c946fe146102b257600080fd5b806323537405146102215780632c01a1e81461024457600080fd5b8063125700111161016057806312570011146101a4578063181f5a77146101cc5780631cdf63431461020e57600080fd5b80630c5801e31461017c578063117392ce14610191575b600080fd5b61018f61018a3660046126db565b6103ca565b005b61018f61019f366004612747565b6106db565b6101b76101b236600461275f565b610926565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101c391906127dc565b61018f61021c3660046127ef565b610939565b61023461022f36600461284a565b6109fc565b6040516101c394939291906128a0565b61018f6102523660046127ef565b610c0f565b610291610265366004612958565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101c3565b61018f6102ad3660046127ef565b610e8d565b6102c56102c036600461275f565b611026565b6040516101c392919061297a565b61018f6102e13660046127ef565b6110e8565b61018f6102f43660046127ef565b6115c9565b61030c61030736600461275f565b611a63565b6040516101c39190612a1c565b61018f610327366004612a6d565b611b49565b61018f611f5d565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c3565b61036f61036a36600461275f565b61205a565b6040516101c39190612b90565b61018f61038a36600461275f565b612104565b61020161039d366004612b9e565b6121cf565b6103aa612287565b6040516101c39190612bc8565b61018f6103c5366004612c38565b6123cc565b828114610412576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b848110156106d357600086868381811061044a5761044a612c55565b905060200201359050600085858481811061046757610467612c55565b90506020028101906104799190612c84565b61048290612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff166104d3576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16331480159061051057503373ffffffffffffffffffffffffffffffffffffffff851614155b15610547576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160008381526007602052604090205473ffffffffffffffffffffffffffffffffffffffff90811691161415806105f9575060208082015160405161058d92016127dc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600783529290922091926105e0926001019101612ea5565b6040516020818303038152906040528051906020012014155b156106c0578051600083815260076020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906106669082612f94565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a8383602001516040516106b79291906130ae565b60405180910390a25b5050806106cc906130f6565b905061042e565b505050505050565b6106e36123e0565b60408051823560208281019190915280840135828401528251808303840181526060909201909252805191012061071b600382612463565b15610752576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107646080840160608501612c38565b73ffffffffffffffffffffffffffffffffffffffff16146108cf5761078f6080830160608401612c38565b73ffffffffffffffffffffffffffffffffffffffff163b158061086f57506107bd6080830160608401612c38565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f884efe6100000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa158015610849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086d919061312e565b155b156108cf576108846080830160608401612c38565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610409565b6108da60038261247e565b50600081815260026020526040902082906108f5828261314b565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b6000610933600583612463565b92915050565b6109416123e0565b60005b818110156109f757600083838381811061096057610960612c55565b60209081029290920135600081815260079093526040832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559093509190506109b16001830182612641565b50506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a1506109f0816130f6565b9050610944565b505050565b63ffffffff81166000908152600960205260408120819060609081908390610a269060030161248a565b90506000815167ffffffffffffffff811115610a4457610a44612cc2565b604051908082528060200260200182016040528015610a8a57816020015b604080518082019091526000815260606020820152815260200190600190039081610a625790505b50905060005b8151811015610bc9576040518060400160405280848381518110610ab657610ab6612c55565b60200260200101518152602001600960008b63ffffffff1663ffffffff1681526020019081526020016000206005016000868581518110610af957610af9612c55565b602002602001015181526020019081526020016000208054610b1a90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610b4690612e58565b8015610b935780601f10610b6857610100808354040283529160200191610b93565b820191906000526020600020905b815481529060010190602001808311610b7657829003601f168201915b5050505050815250828281518110610bad57610bad612c55565b602002602001018190525080610bc2906130f6565b9050610a90565b5063ffffffff8781166000908152600960205260409020805491821691640100000000900460ff1690610bfe9060010161248a565b919750955093509150509193509193565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610e87576000848483818110610c4957610c49612c55565b60209081029290920135600081815260089093526040909220549192505068010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580610cc5576040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260048101839052602401610409565b60008281526008602090815260408083205463ffffffff168352600782528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610d1f90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4b90612e58565b8015610d985780601f10610d6d57610100808354040283529160200191610d98565b820191906000526020600020905b815481529060010190602001808311610d7b57829003601f168201915b505050505081525050905084158015610dc85750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610dff576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526008602052604080822080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610e6b9085815260200190565b60405180910390a150505080610e80906130f6565b9050610c2d565b50505050565b610e956123e0565b60005b818110156109f7576000838383818110610eb457610eb4612c55565b9050602002810190610ec69190612c84565b610ecf90612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff16610f20576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815260008681526007909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815591519091906001820190610fa39082612f94565b50905050600a60008154610fb6906130f6565b909155508151602083015160405173ffffffffffffffffffffffffffffffffffffffff909216917fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b9161100b918591906130ae565b60405180910390a250508061101f906130f6565b9050610e98565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260086020908152838220805463ffffffff808216865273ffffffffffffffffffffffffffffffffffffffff6801000000000000000083041684870152600183015486880152640100000000909104168352600201905291822060608201906110bf9061248a565b905260009384526008602052604090932054929364010000000090930463ffffffff1692915050565b60005b818110156109f757600083838381811061110757611107612c55565b905060200281019061111991906131cd565b61112290613201565b9050600061114560005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916111a290612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546111ce90612e58565b801561121b5780601f106111f05761010080835404028352916020019161121b565b820191906000526020600020905b8154815290600101906020018083116111fe57829003601f168201915b50505050508152505090508115801561124b5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611282576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff161515806112f75783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611348576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361138a57806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b60408581015160009081526008602052208054640100000000900463ffffffff169060046113b7836132f0565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152600860205290812054640100000000900490911691505b82518110156114c25761142c83828151811061141457611414612c55565b6020026020010151600361246390919063ffffffff16565b61146457826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b6114b183828151811061147957611479612c55565b6020908102919091018101516040808b015160009081526008845281812063ffffffff80891683526002909101909452209161247e16565b506114bb816130f6565b90506113f6565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b018051845184529285902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9586160217905592518b5193518551918252939095169085015216908201527f6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e9060600160405180910390a1505050505050806115c2906130f6565b90506110eb565b60005b818110156109f75760008383838181106115e8576115e8612c55565b90506020028101906115fa91906131cd565b61160390613201565b9050600061162660005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff9081168352600182018054969091163314965093949193909284019161168390612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546116af90612e58565b80156116fc5780601f106116d1576101008083540402835291602001916116fc565b820191906000526020600020905b8154815290600101906020018083116116df57829003601f168201915b50505050508152505090508115801561172c5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611763576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580806117a757506040840151155b156117e65783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611837576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361187957806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b604085810151600090815260086020522080546004906118a690640100000000900463ffffffff166132f0565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526008602052908120546401000000009004909116905b82518110156119605761190283828151811061141457611414612c55565b61193a57826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b61194f83828151811061147957611479612c55565b50611959816130f6565b90506118e4565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b0151835183529184902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9094169390930292909217909155905189518351918252909316908301527f5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f910160405180910390a150505050505080611a5c906130f6565b90506115cc565b6040805180820190915260008152606060208201526000828152600760209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611ac090612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054611aec90612e58565b8015611b395780601f10611b0e57610100808354040283529160200191611b39565b820191906000526020600020905b815481529060010190602001808311611b1c57829003601f168201915b5050505050815250509050919050565b611b516123e0565b600b5463ffffffff16600081815260096020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000168317640100000000851515021790555b85811015611c76576000878783818110611bb857611bb8612c55565b905060200201359050611bf581600960008663ffffffff1663ffffffff16815260200190815260200160002060010161246390919063ffffffff16565b15611c3b576040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff8416600482015260248101829052604401610409565b63ffffffff8084166000908152600960205260409020611c6391600190910190839061247e16565b505080611c6f906130f6565b9050611b9c565b5060005b83811015611ed45736858583818110611c9557611c95612c55565b9050602002810190611ca79190612c84565b90508035611cb6600382612463565b611cef576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b611cfa600582612463565b15611d34576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b63ffffffff8085166000908152600960205260409020611d5c91600390910190839061246316565b15611da2576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8516600482015260248101829052604401610409565b60005b88811015611e5a5760008a8a83818110611dc157611dc1612c55565b602090810292909201356000818152600884526040808220805463ffffffff6401000000009091048116845260029091019095529020909350611e0992909150859061246316565b611e49576040517fa7e792500000000000000000000000000000000000000000000000000000000081526004810182905260248101849052604401610409565b50611e53816130f6565b9050611da5565b5063ffffffff8085166000908152600960205260409020611e8391600390910190839061247e16565b50611e916020830183613313565b63ffffffff86166000908152600960209081526040808320868452600501909152902091611ec0919083613378565b50505080611ecd906130f6565b9050611c7a565b50600b8054600090611eeb9063ffffffff166132f0565b91906101000a81548163ffffffff021916908363ffffffff1602179055507fab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a58183604051611f4d92919063ffffffff9290921682521515602082015260400190565b60405180910390a1505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611fde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610409565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff16908111156120c3576120c3612af1565b60018111156120d4576120d4612af1565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b61210c6123e0565b612117600382612463565b612150576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b61215b600582612463565b15612195576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b6121a060058261247e565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff82166000908152600960209081526040808320848452600501909152902080546060919061220190612e58565b80601f016020809104026020016040519081016040528092919081815260200182805461222d90612e58565b801561227a5780601f1061224f5761010080835404028352916020019161227a565b820191906000526020600020905b81548152906001019060200180831161225d57829003601f168201915b5050505050905092915050565b60606000612295600361248a565b905060006122a36005612497565b82516122af9190613493565b67ffffffffffffffff8111156122c7576122c7612cc2565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b83518110156123c357600084828151811061235b5761235b612c55565b6020026020010151905061237981600561246390919063ffffffff16565b6123b2576123868161205a565b84848151811061239857612398612c55565b602002602001018190525082806123ae906130f6565b9350505b506123bc816130f6565b905061233e565b50909392505050565b6123d46123e0565b6123dd816124a1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314612461576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610409565b565b600081815260018301602052604081205415155b9392505050565b60006124778383612596565b60606000612477836125e5565b6000610933825490565b3373ffffffffffffffffffffffffffffffffffffffff821603612520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610409565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008181526001830160205260408120546125dd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610933565b506000610933565b60608160000180548060200260200160405190810160405280929190818152602001828054801561263557602002820191906000526020600020905b815481526020019060010190808311612621575b50505050509050919050565b50805461264d90612e58565b6000825580601f1061265d575050565b601f0160209004906000526020600020908101906123dd91905b8082111561268b5760008155600101612677565b5090565b60008083601f8401126126a157600080fd5b50813567ffffffffffffffff8111156126b957600080fd5b6020830191508360208260051b85010111156126d457600080fd5b9250929050565b600080600080604085870312156126f157600080fd5b843567ffffffffffffffff8082111561270957600080fd5b6127158883890161268f565b9096509450602087013591508082111561272e57600080fd5b5061273b8782880161268f565b95989497509550505050565b60006080828403121561275957600080fd5b50919050565b60006020828403121561277157600080fd5b5035919050565b6000815180845260005b8181101561279e57602081850181015186830182015201612782565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006124776020830184612778565b6000806020838503121561280257600080fd5b823567ffffffffffffffff81111561281957600080fd5b6128258582860161268f565b90969095509350505050565b803563ffffffff8116811461284557600080fd5b919050565b60006020828403121561285c57600080fd5b61247782612831565b600081518084526020808501945080840160005b8381101561289557815187529582019590820190600101612879565b509495945050505050565b63ffffffff85168152600060208515158184015260406080818501526128c96080850187612865565b8481036060860152855180825283820190600581901b8301850185890160005b83811015612946578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001855281518051845288015188840188905261293388850182612778565b95890195935050908701906001016128e9565b50909c9b505050505050505050505050565b6000806040838503121561296b57600080fd5b50508035926020909101359150565b60408152600060c0820163ffffffff8551166040840152602073ffffffffffffffffffffffffffffffffffffffff81870151166060850152604086015160808501526060860151608060a086015282815180855260e0870191508383019450600092505b808310156129fe57845182529383019360019290920191908301906129de565b5063ffffffff8716838701529350612a139050565b50509392505050565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152612a576060840182612778565b949350505050565b80151581146123dd57600080fd5b600080600080600060608688031215612a8557600080fd5b853567ffffffffffffffff80821115612a9d57600080fd5b612aa989838a0161268f565b90975095506020880135915080821115612ac257600080fd5b50612acf8882890161268f565b9094509250506040860135612ae381612a5f565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8051825260208101516020830152604081015160028110612b6a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109338284612b20565b60008060408385031215612bb157600080fd5b612bba83612831565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b81811015612c0a57612bf7838551612b20565b9284019260809290920191600101612be4565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146123dd57600080fd5b600060208284031215612c4a57600080fd5b813561247781612c16565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112612cb857600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715612d1457612d14612cc2565b60405290565b6040516080810167ffffffffffffffff81118282101715612d1457612d14612cc2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612d8457612d84612cc2565b604052919050565b600060408236031215612d9e57600080fd5b612da6612cf1565b8235612db181612c16565b815260208381013567ffffffffffffffff80821115612dcf57600080fd5b9085019036601f830112612de257600080fd5b813581811115612df457612df4612cc2565b612e24847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612d3d565b91508082523684828501011115612e3a57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680612e6c57607f821691505b602082108103612759577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454612eb981612e58565b80848701526040600180841660008114612eda5760018114612f1257612f40565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550612f40565b896000528660002060005b85811015612f385781548b8201860152908301908801612f1d565b8a0184019650505b509398975050505050505050565b601f8211156109f757600081815260208120601f850160051c81016020861015612f755750805b601f850160051c820191505b818110156106d357828155600101612f81565b815167ffffffffffffffff811115612fae57612fae612cc2565b612fc281612fbc8454612e58565b84612f4e565b602080601f8311600181146130155760008415612fdf5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556106d3565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561306257888601518255948401946001909101908401613043565b508582101561309e57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000612a576040830184612778565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613127576131276130c7565b5060010190565b60006020828403121561314057600080fd5b815161247781612a5f565b81358155602082013560018201556002810160408301356002811061316f57600080fd5b8154606085013561317f81612c16565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112612cb857600080fd5b60006080823603121561321357600080fd5b61321b612d1a565b61322483612831565b815260208084013561323581612c16565b8282015260408481013590830152606084013567ffffffffffffffff8082111561325e57600080fd5b9085019036601f83011261327157600080fd5b81358181111561328357613283612cc2565b8060051b9150613294848301612d3d565b81815291830184019184810190368411156132ae57600080fd5b938501935b838510156132cc578435825293850193908501906132b3565b606087015250939695505050505050565b6020815260006124776020830184612865565b600063ffffffff808316818103613309576133096130c7565b6001019392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261334857600080fd5b83018035915067ffffffffffffffff82111561336357600080fd5b6020019150368190038213156126d457600080fd5b67ffffffffffffffff83111561339057613390612cc2565b6133a48361339e8354612e58565b83612f4e565b6000601f8411600181146133f657600085156133c05750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561348c565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156134455786850135825560209485019460019092019101613425565b5086821015613480577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b81810381811115610933576109336130c756fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32[]\",\"name\":\"\",\"type\":\"uint32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052601280546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61437580620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806365c14dc7116100ee5780639cb7c5f411610097578063c63239c511610071578063c63239c514610413578063ddbe4f8214610426578063e29581aa1461043b578063f2fde38b1461045157600080fd5b80639cb7c5f4146103cd578063ae3c241c146103ed578063b06e07a71461040057600080fd5b806373ac22b4116100c857806373ac22b41461038a57806379ba50971461039d5780638da5cb5b146103a557600080fd5b806365c14dc71461034257806366acaa33146103625780636ae5c5911461037757600080fd5b8063214502431161015b57806336b402fb1161013557806336b402fb146102b3578063398f3773146102fb57806350c946fe1461030e5780635e65e3091461032f57600080fd5b8063214502431461026b57806323537405146102805780632c01a1e8146102a057600080fd5b8063181f5a771161018c578063181f5a77146102035780631cdf6343146102455780631d05394c1461025857600080fd5b80630c5801e3146101b3578063117392ce146101c857806312570011146101db575b600080fd5b6101c66101c136600461328f565b610464565b005b6101c66101d63660046132fb565b610775565b6101ee6101e9366004613313565b6109c0565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101fa9190613390565b6101c66102533660046133a3565b6109d3565b6101c66102663660046133a3565b610aa3565b610273610be3565b6040516101fa91906134f5565b61029361028e36600461358e565b610d32565b6040516101fa91906135a9565b6101c66102ae3660046133a3565b610d65565b6102ed6102c13660046135bc565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101fa565b6101c66103093660046133a3565b611009565b61032161031c366004613313565b6111cc565b6040516101fa92919061361f565b6101c661033d3660046133a3565b611201565b610355610350366004613313565b611704565b6040516101fa919061367c565b61036a6117ea565b6040516101fa919061368f565b6101c6610385366004613710565b6119a8565b6101c66103983660046133a3565b611a4b565b6101c6611eb1565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101fa565b6103e06103db366004613313565b611fae565b6040516101fa9190613833565b6101c66103fb366004613313565b612058565b61023861040e366004613841565b612123565b6101c661042136600461386b565b6121f8565b61042e612287565b6040516101fa91906138fe565b61044361244d565b6040516101fa92919061394c565b6101c661045f366004613a2d565b6125d8565b8281146104ac576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b8481101561076d5760008686838181106104e4576104e4613a4a565b905060200201359050600085858481811061050157610501613a4a565b90506020028101906105139190613a79565b61051c90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661056d576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff1633148015906105aa57503373ffffffffffffffffffffffffffffffffffffffff851614155b156105e1576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516000838152600f602052604090205473ffffffffffffffffffffffffffffffffffffffff908116911614158061069357506020808201516040516106279201613390565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600f835292909220919261067a926001019101613c9a565b6040516020818303038152906040528051906020012014155b1561075a5780516000838152600f6020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906107009082613d89565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a838360200151604051610751929190613ea3565b60405180910390a25b50508061076690613eeb565b90506104c8565b505050505050565b61077d6125ec565b6040805182356020828101919091528084013582840152825180830384018152606090920190925280519101206107b560038261266f565b156107ec576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107fe6080840160608501613a2d565b73ffffffffffffffffffffffffffffffffffffffff1614610969576108296080830160608401613a2d565b73ffffffffffffffffffffffffffffffffffffffff163b158061090957506108576080830160608401613a2d565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f73e8b41d00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa1580156108e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109079190613f23565b155b156109695761091e6080830160608401613a2d565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016104a3565b61097460038261268a565b506000818152600260205260409020829061098f8282613f40565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b60006109cd60058361266f565b92915050565b6109db6125ec565b60005b81811015610a9e5760008383838181106109fa576109fa613a4a565b602090810292909201356000818152600f9093526040832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155909350919050610a4b60018301826131f5565b50610a59905060078261268a565b506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a150610a9781613eeb565b90506109de565b505050565b610aab6125ec565b60005b81811015610a9e576000838383818110610aca57610aca613a4a565b9050602002016020810190610adf919061358e565b63ffffffff808216600090815260116020526040812080549394509264010000000090049091169003610b46576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024016104a3565b63ffffffff808316600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffff000000000000000000169055610b90916009919061268a16565b506040805163ffffffff84168152600060208201527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a1505080610bdc90613eeb565b9050610aae565b601254606090640100000000900463ffffffff1660006001610c056009612696565b601254610c209190640100000000900463ffffffff16613fc2565b610c2a9190613fc2565b67ffffffffffffffff811115610c4257610c42613ab7565b604051908082528060200260200182016040528015610cb857816020015b6040805160a08101825260008082526020808301829052928201526060808201819052608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181610c605790505b509050600060015b838163ffffffff161015610d2957610ce2600963ffffffff8084169061266f16565b610d1957610cef816126a0565b838381518110610d0157610d01613a4a565b602002602001018190525081610d1690613eeb565b91505b610d2281613fd5565b9050610cc0565b50909392505050565b6040805160a08101825260008082526020820181905291810191909152606080820181905260808201526109cd826126a0565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015611003576000848483818110610d9f57610d9f613a4a565b602090810292909201356000818152601090935260409092206001015491925050151580610dfc576040517f64e2ee92000000000000000000000000000000000000000000000000000000008152600481018390526024016104a3565b60008281526010602090815260408083205463ffffffff168352600f82528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610e5690613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054610e8290613c4d565b8015610ecf5780601f10610ea457610100808354040283529160200191610ecf565b820191906000526020600020905b815481529060010190602001808311610eb257829003601f168201915b505050505081525050905084158015610eff5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610f36576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040902060010154610f5490600b90612939565b50600083815260106020526040902060020154610f7390600d90612939565b5060008381526010602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001681556001810183905560020191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610fe79085815260200190565b60405180910390a150505080610ffc90613eeb565b9050610d83565b50505050565b6110116125ec565b60005b81811015610a9e57600083838381811061103057611030613a4a565b90506020028101906110429190613a79565b61104b90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661109c576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600f909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815592519192909160018201906111289082613d89565b5050601280549091506000906111439063ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff167fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b8284602001516040516111b1929190613ea3565b60405180910390a25050806111c590613eeb565b9050611014565b60408051608081018252600080825260208201819052918101829052606080820152906111f883612945565b91509150915091565b60005b81811015610a9e57600083838381811061122057611220613a4a565b90506020028101906112329190613ff8565b61123b9061402c565b9050600061125e60005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916112bb90613c4d565b80601f01602080910402602001604051908101604052809291908181526020018280546112e790613c4d565b80156113345780601f1061130957610100808354040283529160200191611334565b820191906000526020600020905b81548152906001019060200180831161131757829003601f168201915b5050505050815250509050811580156113645750805173ffffffffffffffffffffffffffffffffffffffff163314155b1561139b576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040808401516000908152601060205220600101541515806113f15783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b6020840151158061143757508360200151601060008660400151815260200190815260200160002060010154141580156114375750602084015161143790600b9061266f565b1561146e576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606084015180516000036114b057806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054640100000000900463ffffffff169060046114dd83613fd5565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152601060205290812054640100000000900490911691505b82518110156115e85761155283828151811061153a5761153a613a4a565b6020026020010151600361266f90919063ffffffff16565b61158a57826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b6115d783828151811061159f5761159f613a4a565b6020908102919091018101516040808b015160009081526010845281812063ffffffff80891683526003909101909452209161268a16565b506115e181613eeb565b905061151c565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9096169590951790945581518082528382206002015581518152828120600190810154948b015192518252929020909101541461169d5761166c600b82612939565b50602080880180516040808b015160009081526010909452909220600101919091555161169b90600b9061268a565b505b60408781015188516020808b0151845193845263ffffffff909216908301528183015290517ff101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d09169181900360600190a150505050505050806116fd90613eeb565b9050611204565b6040805180820190915260008152606060208201526000828152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161176190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461178d90613c4d565b80156117da5780601f106117af576101008083540402835291602001916117da565b820191906000526020600020905b8154815290600101906020018083116117bd57829003601f168201915b5050505050815250509050919050565b60125460609063ffffffff16600060016118046007612696565b601254611817919063ffffffff16613fc2565b6118219190613fc2565b67ffffffffffffffff81111561183957611839613ab7565b60405190808252806020026020018201604052801561187f57816020015b6040805180820190915260008152606060208201528152602001906001900390816118575790505b509050600060015b8363ffffffff16811015610d29576118a060078261266f565b611998576000818152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff16835260018101805491928401916118ec90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461191890613c4d565b80156119655780601f1061193a57610100808354040283529160200191611965565b820191906000526020600020905b81548152906001019060200180831161194857829003601f168201915b50505050508152505083838151811061198057611980613a4a565b60200260200101819052508161199590613eeb565b91505b6119a181613eeb565b9050611887565b6119b06125ec565b601254640100000000900463ffffffff16600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001682179055611a0781600188888888886129ea565b60128054600490611a2590640100000000900463ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050565b60005b81811015610a9e576000838383818110611a6a57611a6a613a4a565b9050602002810190611a7c9190613ff8565b611a859061402c565b90506000611aa860005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff90811683526001820180549690911633149650939491939092840191611b0590613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054611b3190613c4d565b8015611b7e5780601f10611b5357610100808354040283529160200191611b7e565b820191906000526020600020905b815481529060010190602001808311611b6157829003601f168201915b505050505081525050905081158015611bae5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611be5576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526010602052206001015415158080611c0a57506040840151155b15611c495783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b60208401511580611c6657506020840151611c6690600b9061266f565b15611c9d576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401518051600003611cdf57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054600490611d0c90640100000000900463ffffffff16613fd5565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526010602052908120546401000000009004909116905b8251811015611dc657611d6883828151811061153a5761153a613a4a565b611da057826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b611db583828151811061159f5761159f613a4a565b50611dbf81613eeb565b9050611d4a565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9687161790558251808352848320600201558a018051925182529290206001015551611e3891600b919061268a16565b506040860151611e4a90600d9061268a565b5060408681015187516020808a0151845193845263ffffffff909216908301528183015290517fc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da9181900360600190a150505050505080611eaa90613eeb565b9050611a4e565b60015473ffffffffffffffffffffffffffffffffffffffff163314611f32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016104a3565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff169081111561201757612017613794565b600181111561202857612028613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b6120606125ec565b61206b60038261266f565b6120a4576040517fe181733f000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120af60058261266f565b156120e9576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120f460058261268a565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff8083166000908152601160209081526040808320805464010000000090049094168084526001909401825280832085845260030190915290208054606092919061217190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461219d90613c4d565b80156121ea5780601f106121bf576101008083540402835291602001916121ea565b820191906000526020600020905b8154815290600101906020018083116121cd57829003601f168201915b505050505091505092915050565b6122006125ec565b63ffffffff808716600090815260116020526040812054640100000000900490911690819003612264576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff881660048201526024016104a3565b61227e8761227183613fd5565b92508288888888886129ea565b50505050505050565b606060006122956003612e76565b905060006122a36005612696565b82516122af9190613fc2565b67ffffffffffffffff8111156122c7576122c7613ab7565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b8351811015610d2957600084828151811061235b5761235b613a4a565b6020026020010151905061237981600561266f90919063ffffffff16565b61243c576002600082815260200190815260200160002060405180608001604052908160008201548152602001600182015481526020016002820160009054906101000a900460ff1660018111156123d3576123d3613794565b60018111156123e4576123e4613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff16602090910152845185908590811061242257612422613a4a565b6020026020010181905250828061243890613eeb565b9350505b5061244681613eeb565b905061233e565b606080600061245c600d612e76565b90506000815167ffffffffffffffff81111561247a5761247a613ab7565b6040519080825280602002602001820160405280156124e957816020015b60408051608081018252600080825260208083018290529282015260608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816124985790505b5090506000825167ffffffffffffffff81111561250857612508613ab7565b604051908082528060200260200182016040528015612531578160200160208202803683370190505b50905060005b83518110156125cd57600084828151811061255457612554613a4a565b6020026020010151905060008061256a83612945565b915091508186858151811061258157612581613a4a565b60200260200101819052508085858151811061259f5761259f613a4a565b602002602001019063ffffffff16908163ffffffff1681525050505050806125c690613eeb565b9050612537565b509094909350915050565b6125e06125ec565b6125e981612e83565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461266d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016104a3565b565b600081815260018301602052604081205415155b9392505050565b60006126838383612f78565b60006109cd825490565b6040805160a081018252600080825260208083018290528284018290526060808401819052608084015263ffffffff85811683526011825284832080546401000000009004909116808452600190910182528483206002810180548751818602810186019098528088529596929591949390919083018282801561274357602002820191906000526020600020905b81548152602001906001019080831161272f575b505050505090506000815167ffffffffffffffff81111561276657612766613ab7565b6040519080825280602002602001820160405280156127ac57816020015b6040805180820190915260008152606060208201528152602001906001900390816127845790505b50905060005b81518110156128cd5760405180604001604052808483815181106127d8576127d8613a4a565b602002602001015181526020018560030160008685815181106127fd576127fd613a4a565b60200260200101518152602001908152602001600020805461281e90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461284a90613c4d565b80156128975780601f1061286c57610100808354040283529160200191612897565b820191906000526020600020905b81548152906001019060200180831161287a57829003601f168201915b50505050508152508282815181106128b1576128b1613a4a565b6020026020010181905250806128c690613eeb565b90506127b2565b506040805160a08101825263ffffffff888116600081815260116020818152868320548086168752948b168187015292909152905268010000000000000000900460ff161515918101919091526060810161292785612e76565b81526020019190915295945050505050565b60006126838383612fc7565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260106020908152838220805463ffffffff8082168652600183015484870152600283015486880152640100000000909104168352600301905291822060608201906129c190612e76565b905260009384526010602052604090932054929364010000000090930463ffffffff1692915050565b63ffffffff8088166000908152601160209081526040808320938a16835260019093019052908120905b85811015612ad857612a41878783818110612a3157612a31613a4a565b859260209091020135905061266f565b15612aa25788878783818110612a5957612a59613a4a565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff909416600485015260200291909101356024830152506044016104a3565b612ac7878783818110612ab757612ab7613a4a565b859260209091020135905061268a565b50612ad181613eeb565b9050612a14565b5060005b83811015612db75736858583818110612af757612af7613a4a565b9050602002810190612b099190613a79565b9050612b176003823561266f565b612b50576040517fe181733f000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b612b5c6005823561266f565b15612b96576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b8035600090815260038401602052604081208054612bb390613c4d565b90501115612bfc576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152813560248201526044016104a3565b60005b87811015612d0e57612ca38235601060008c8c86818110612c2257612c22613a4a565b9050602002013581526020019081526020016000206003016000601060008e8e88818110612c5257612c52613a4a565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002061266f90919063ffffffff16565b612cfe57888882818110612cb957612cb9613a4a565b6040517fa7e7925000000000000000000000000000000000000000000000000000000000815260209091029290920135600483015250823560248201526044016104a3565b612d0781613eeb565b9050612bff565b5060028301805460018101825560009182526020918290208335910155612d3790820182614137565b82356000908152600386016020526040902091612d5591908361419c565b50612da68a8a83358b8b612d6c6020880188614137565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506130ba92505050565b50612db081613eeb565b9050612adc565b5063ffffffff88811660008181526011602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff1668010000000000000000881515027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1617640100000000958d1695860217905581519283528201929092527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a15050505050505050565b6060600061268383613199565b3373ffffffffffffffffffffffffffffffffffffffff821603612f02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016104a3565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054612fbf575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109cd565b5060006109cd565b600081815260018301602052604081205480156130b0576000612feb600183613fc2565b8554909150600090612fff90600190613fc2565b905081811461306457600086600001828154811061301f5761301f613a4a565b906000526020600020015490508087600001848154811061304257613042613a4a565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613075576130756142b7565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109cd565b60009150506109cd565b60008481526002602081905260409091200154610100900473ffffffffffffffffffffffffffffffffffffffff161561076d57600084815260026020819052604091829020015490517ffba64a7c00000000000000000000000000000000000000000000000000000000815261010090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c9061315f908690869086908b908d906004016142e6565b600060405180830381600087803b15801561317957600080fd5b505af115801561318d573d6000803e3d6000fd5b50505050505050505050565b6060816000018054806020026020016040519081016040528092919081815260200182805480156131e957602002820191906000526020600020905b8154815260200190600101908083116131d5575b50505050509050919050565b50805461320190613c4d565b6000825580601f10613211575050565b601f0160209004906000526020600020908101906125e991905b8082111561323f576000815560010161322b565b5090565b60008083601f84011261325557600080fd5b50813567ffffffffffffffff81111561326d57600080fd5b6020830191508360208260051b850101111561328857600080fd5b9250929050565b600080600080604085870312156132a557600080fd5b843567ffffffffffffffff808211156132bd57600080fd5b6132c988838901613243565b909650945060208701359150808211156132e257600080fd5b506132ef87828801613243565b95989497509550505050565b60006080828403121561330d57600080fd5b50919050565b60006020828403121561332557600080fd5b5035919050565b6000815180845260005b8181101561335257602081850181015186830182015201613336565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000612683602083018461332c565b600080602083850312156133b657600080fd5b823567ffffffffffffffff8111156133cd57600080fd5b6133d985828601613243565b90969095509350505050565b600081518084526020808501945080840160005b83811015613415578151875295820195908201906001016133f9565b509495945050505050565b600063ffffffff8083511684526020818185015116818601526040915081840151151582860152606084015160a0606087015261346060a08701826133e5565b9050608085015186820360808801528181518084528484019150848160051b850101858401935060005b828110156134e7578582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00184528451805183528701518783018990526134d48984018261332c565b958801959488019492505060010161348a565b509998505050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613556858351613420565b9450928501929085019060010161351c565b5092979650505050505050565b803563ffffffff8116811461358957600080fd5b919050565b6000602082840312156135a057600080fd5b61268382613575565b6020815260006126836020830184613420565b600080604083850312156135cf57600080fd5b50508035926020909101359150565b63ffffffff81511682526020810151602083015260408101516040830152600060608201516080606085015261361760808501826133e5565b949350505050565b60408152600061363260408301856135de565b905063ffffffff831660208301529392505050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152613617604085018261332c565b6020815260006126836020830184613647565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526136f0858351613647565b945092850192908501906001016136b6565b80151581146125e957600080fd5b60008060008060006060868803121561372857600080fd5b853567ffffffffffffffff8082111561374057600080fd5b61374c89838a01613243565b9097509550602088013591508082111561376557600080fd5b5061377288828901613243565b909450925050604086013561378681613702565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526020810151602083015260408101516002811061380d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109cd82846137c3565b6000806040838503121561385457600080fd5b61385d83613575565b946020939093013593505050565b6000806000806000806080878903121561388457600080fd5b61388d87613575565b9550602087013567ffffffffffffffff808211156138aa57600080fd5b6138b68a838b01613243565b909750955060408901359150808211156138cf57600080fd5b506138dc89828a01613243565b90945092505060608701356138f081613702565b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b818110156139405761392d8385516137c3565b928401926080929092019160010161391a565b50909695505050505050565b6000604082016040835280855180835260608501915060608160051b8601019250602080880160005b838110156139c1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08887030185526139af8683516135de565b95509382019390820190600101613975565b50508584038187015286518085528782019482019350915060005b828110156139fe57845163ffffffff16845293810193928101926001016139dc565b5091979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146125e957600080fd5b600060208284031215613a3f57600080fd5b813561268381613a0b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112613aad57600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613b0957613b09613ab7565b60405290565b6040516080810167ffffffffffffffff81118282101715613b0957613b09613ab7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b7957613b79613ab7565b604052919050565b600060408236031215613b9357600080fd5b613b9b613ae6565b8235613ba681613a0b565b815260208381013567ffffffffffffffff80821115613bc457600080fd5b9085019036601f830112613bd757600080fd5b813581811115613be957613be9613ab7565b613c19847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613b32565b91508082523684828501011115613c2f57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680613c6157607f821691505b60208210810361330d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454613cae81613c4d565b80848701526040600180841660008114613ccf5760018114613d0757613d35565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550613d35565b896000528660002060005b85811015613d2d5781548b8201860152908301908801613d12565b8a0184019650505b509398975050505050505050565b601f821115610a9e57600081815260208120601f850160051c81016020861015613d6a5750805b601f850160051c820191505b8181101561076d57828155600101613d76565b815167ffffffffffffffff811115613da357613da3613ab7565b613db781613db18454613c4d565b84613d43565b602080601f831160018114613e0a5760008415613dd45750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561076d565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613e5757888601518255948401946001909101908401613e38565b5085821015613e9357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000613617604083018461332c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613f1c57613f1c613ebc565b5060010190565b600060208284031215613f3557600080fd5b815161268381613702565b813581556020820135600182015560028101604083013560028110613f6457600080fd5b81546060850135613f7481613a0b565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b818103818111156109cd576109cd613ebc565b600063ffffffff808316818103613fee57613fee613ebc565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112613aad57600080fd5b60006080823603121561403e57600080fd5b614046613b0f565b61404f83613575565b81526020808401358183015260408401356040830152606084013567ffffffffffffffff8082111561408057600080fd5b9085019036601f83011261409357600080fd5b8135818111156140a5576140a5613ab7565b8060051b91506140b6848301613b32565b81815291830184019184810190368411156140d057600080fd5b938501935b838510156140ee578435825293850193908501906140d5565b606087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156139405783518352928401929184019160010161411b565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261416c57600080fd5b83018035915067ffffffffffffffff82111561418757600080fd5b60200191503681900382131561328857600080fd5b67ffffffffffffffff8311156141b4576141b4613ab7565b6141c8836141c28354613c4d565b83613d43565b6000601f84116001811461421a57600085156141e45750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556142b0565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156142695786850135825560209485019460019092019101614249565b50868210156142a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561431f57600080fd5b8560051b808860a0850137820182810360a090810160208501526143459082018761332c565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000813000a", } var CapabilityRegistryABI = CapabilityRegistryMetaData.ABI @@ -239,28 +247,25 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetCapability(hashed return _CapabilityRegistry.Contract.GetCapability(&_CapabilityRegistry.CallOpts, hashedId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getDON", donId) if err != nil { - return *new(uint32), *new(bool), *new([][32]byte), *new([]CapabilityRegistryCapabilityConfiguration), err + return *new(CapabilityRegistryDONInfo), err } - out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) - out1 := *abi.ConvertType(out[1], new(bool)).(*bool) - out2 := *abi.ConvertType(out[2], new([][32]byte)).(*[][32]byte) - out3 := *abi.ConvertType(out[3], new([]CapabilityRegistryCapabilityConfiguration)).(*[]CapabilityRegistryCapabilityConfiguration) + out0 := *abi.ConvertType(out[0], new(CapabilityRegistryDONInfo)).(*CapabilityRegistryDONInfo) - return out0, out1, out2, out3, err + return out0, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } @@ -286,6 +291,28 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONCapabilityConf return _CapabilityRegistry.Contract.GetDONCapabilityConfig(&_CapabilityRegistry.CallOpts, donId, capabilityId) } +func (_CapabilityRegistry *CapabilityRegistryCaller) GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getDONs") + + if err != nil { + return *new([]CapabilityRegistryDONInfo), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryDONInfo)).(*[]CapabilityRegistryDONInfo) + + return out0, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetDONs() ([]CapabilityRegistryDONInfo, error) { + return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONs() ([]CapabilityRegistryDONInfo, error) { + return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) +} + func (_CapabilityRegistry *CapabilityRegistryCaller) GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getHashedCapabilityId", labelledName, version) @@ -308,26 +335,26 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetHashedCapabilityI return _CapabilityRegistry.Contract.GetHashedCapabilityId(&_CapabilityRegistry.CallOpts, labelledName, version) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getNode", p2pId) if err != nil { - return *new(CapabilityRegistryNodeParams), *new(uint32), err + return *new(CapabilityRegistryNodeInfo), *new(uint32), err } - out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeParams)).(*CapabilityRegistryNodeParams) + out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeInfo)).(*CapabilityRegistryNodeInfo) out1 := *abi.ConvertType(out[1], new(uint32)).(*uint32) return out0, out1, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } @@ -353,6 +380,51 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperator(node return _CapabilityRegistry.Contract.GetNodeOperator(&_CapabilityRegistry.CallOpts, nodeOperatorId) } +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getNodeOperators") + + if err != nil { + return *new([]CapabilityRegistryNodeOperator), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeOperator)).(*[]CapabilityRegistryNodeOperator) + + return out0, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { + return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { + return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getNodes") + + if err != nil { + return *new([]CapabilityRegistryNodeInfo), *new([]uint32), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeInfo)).(*[]CapabilityRegistryNodeInfo) + out1 := *abi.ConvertType(out[1], new([]uint32)).(*[]uint32) + + return out0, out1, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { + return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { + return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) +} + func (_CapabilityRegistry *CapabilityRegistryCaller) IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "isCapabilityDeprecated", hashedCapabilityId) @@ -467,15 +539,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodeOperators return _CapabilityRegistry.Contract.AddNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "addNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -491,6 +563,18 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) DeprecateCapabil return _CapabilityRegistry.Contract.DeprecateCapability(&_CapabilityRegistry.TransactOpts, hashedCapabilityId) } +func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.contract.Transact(opts, "removeDONs", donIds) +} + +func (_CapabilityRegistry *CapabilityRegistrySession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) +} + +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) +} + func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "removeNodeOperators", nodeOperatorIds) } @@ -527,6 +611,18 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) TransferOwnershi return _CapabilityRegistry.Contract.TransferOwnership(&_CapabilityRegistry.TransactOpts, to) } +func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.contract.Transact(opts, "updateDON", donId, nodes, capabilityConfigurations, isPublic) +} + +func (_CapabilityRegistry *CapabilityRegistrySession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) +} + +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) +} + func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodeOperators", nodeOperatorIds, nodeOperators) } @@ -539,15 +635,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodeOperat return _CapabilityRegistry.Contract.UpdateNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperatorIds, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -805,8 +901,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseCapabilityDeprecated return event, nil } -type CapabilityRegistryDONAddedIterator struct { - Event *CapabilityRegistryDONAdded +type CapabilityRegistryConfigSetIterator struct { + Event *CapabilityRegistryConfigSet contract *bind.BoundContract event string @@ -817,7 +913,7 @@ type CapabilityRegistryDONAddedIterator struct { fail error } -func (it *CapabilityRegistryDONAddedIterator) Next() bool { +func (it *CapabilityRegistryConfigSetIterator) Next() bool { if it.fail != nil { return false @@ -826,7 +922,7 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryDONAdded) + it.Event = new(CapabilityRegistryConfigSet) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -841,7 +937,7 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryDONAdded) + it.Event = new(CapabilityRegistryConfigSet) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -856,33 +952,33 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { } } -func (it *CapabilityRegistryDONAddedIterator) Error() error { +func (it *CapabilityRegistryConfigSetIterator) Error() error { return it.fail } -func (it *CapabilityRegistryDONAddedIterator) Close() error { +func (it *CapabilityRegistryConfigSetIterator) Close() error { it.sub.Unsubscribe() return nil } -type CapabilityRegistryDONAdded struct { - DonId *big.Int - IsPublic bool - Raw types.Log +type CapabilityRegistryConfigSet struct { + DonId uint32 + ConfigCount uint32 + Raw types.Log } -func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) { - logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "DONAdded") + logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "ConfigSet") if err != nil { return nil, err } - return &CapabilityRegistryDONAddedIterator{contract: _CapabilityRegistry.contract, event: "DONAdded", logs: logs, sub: sub}, nil + return &CapabilityRegistryConfigSetIterator{contract: _CapabilityRegistry.contract, event: "ConfigSet", logs: logs, sub: sub}, nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) { - logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "DONAdded") + logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "ConfigSet") if err != nil { return nil, err } @@ -892,8 +988,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind. select { case log := <-logs: - event := new(CapabilityRegistryDONAdded) - if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { + event := new(CapabilityRegistryConfigSet) + if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { return err } event.Raw = log @@ -914,9 +1010,9 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind. }), nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) { - event := new(CapabilityRegistryDONAdded) - if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { +func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) { + event := new(CapabilityRegistryConfigSet) + if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { return nil, err } event.Raw = log @@ -986,6 +1082,7 @@ func (it *CapabilityRegistryNodeAddedIterator) Close() error { type CapabilityRegistryNodeAdded struct { P2pId [32]byte NodeOperatorId *big.Int + Signer [32]byte Raw types.Log } @@ -1596,7 +1693,7 @@ func (it *CapabilityRegistryNodeUpdatedIterator) Close() error { type CapabilityRegistryNodeUpdated struct { P2pId [32]byte NodeOperatorId *big.Int - Signer common.Address + Signer [32]byte Raw types.Log } @@ -1930,8 +2027,8 @@ func (_CapabilityRegistry *CapabilityRegistry) ParseLog(log types.Log) (generate return _CapabilityRegistry.ParseCapabilityAdded(log) case _CapabilityRegistry.abi.Events["CapabilityDeprecated"].ID: return _CapabilityRegistry.ParseCapabilityDeprecated(log) - case _CapabilityRegistry.abi.Events["DONAdded"].ID: - return _CapabilityRegistry.ParseDONAdded(log) + case _CapabilityRegistry.abi.Events["ConfigSet"].ID: + return _CapabilityRegistry.ParseConfigSet(log) case _CapabilityRegistry.abi.Events["NodeAdded"].ID: return _CapabilityRegistry.ParseNodeAdded(log) case _CapabilityRegistry.abi.Events["NodeOperatorAdded"].ID: @@ -1962,12 +2059,12 @@ func (CapabilityRegistryCapabilityDeprecated) Topic() common.Hash { return common.HexToHash("0xdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf21") } -func (CapabilityRegistryDONAdded) Topic() common.Hash { - return common.HexToHash("0xab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a5") +func (CapabilityRegistryConfigSet) Topic() common.Hash { + return common.HexToHash("0xf264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651") } func (CapabilityRegistryNodeAdded) Topic() common.Hash { - return common.HexToHash("0x5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f") + return common.HexToHash("0xc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da") } func (CapabilityRegistryNodeOperatorAdded) Topic() common.Hash { @@ -1987,7 +2084,7 @@ func (CapabilityRegistryNodeRemoved) Topic() common.Hash { } func (CapabilityRegistryNodeUpdated) Topic() common.Hash { - return common.HexToHash("0x6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e") + return common.HexToHash("0xf101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d0916") } func (CapabilityRegistryOwnershipTransferRequested) Topic() common.Hash { @@ -2007,16 +2104,22 @@ type CapabilityRegistryInterface interface { GetCapability(opts *bind.CallOpts, hashedId [32]byte) (CapabilityRegistryCapability, error) - GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) + GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) GetDONCapabilityConfig(opts *bind.CallOpts, donId uint32, capabilityId [32]byte) ([]byte, error) + GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) + GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) - GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) + GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) GetNodeOperator(opts *bind.CallOpts, nodeOperatorId *big.Int) (CapabilityRegistryNodeOperator, error) + GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) + + GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) + IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -2031,19 +2134,23 @@ type CapabilityRegistryInterface interface { AddNodeOperators(opts *bind.TransactOpts, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) + AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) DeprecateCapability(opts *bind.TransactOpts, hashedCapabilityId [32]byte) (*types.Transaction, error) + RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) + RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) RemoveNodes(opts *bind.TransactOpts, removedNodeP2PIds [][32]byte) (*types.Transaction, error) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) + UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) + UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) FilterCapabilityAdded(opts *bind.FilterOpts, hashedCapabilityId [][32]byte) (*CapabilityRegistryCapabilityAddedIterator, error) @@ -2057,11 +2164,11 @@ type CapabilityRegistryInterface interface { ParseCapabilityDeprecated(log types.Log) (*CapabilityRegistryCapabilityDeprecated, error) - FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) + FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) - WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) - ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) + ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) FilterNodeAdded(opts *bind.FilterOpts) (*CapabilityRegistryNodeAddedIterator, error) diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 54d1355c62f..c58901795ce 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ GETH_VERSION: 1.13.8 forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin ed9164cfe4619dff824b11df46b66f4c6834b2ca072923f10d9ebc57ce508ed8 -keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin d4e0661491c2adc7f0d7553287c938fb32664b9f07770f6d57ae6511ce9884cd +keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin 0a79d0eba13fd4a4b83d7618bb181c21c42222f3cc6c5a90a09302f685555033 ocr3_capability: ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.bin 9dcbdf55bd5729ba266148da3f17733eb592c871c2108ccca546618628fd9ad2 From bac1f8c78f2f10bb34c49ca79ef7024d306456bb Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 27 May 2024 12:51:01 +0200 Subject: [PATCH 06/19] fix foundry dependency version (#13331) * fix foundry dependency version * update snapshots --- .github/workflows/solidity-foundry.yml | 2 +- contracts/foundry-lib/forge-std | 2 +- .../gas-snapshots/functions.gas-snapshot | 4 +- contracts/gas-snapshots/keystone.gas-snapshot | 65 +++++++++++++++++++ .../operatorforwarder.gas-snapshot | 8 +-- contracts/gas-snapshots/shared.gas-snapshot | 4 +- 6 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 contracts/gas-snapshots/keystone.gas-snapshot diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index 5e2d95ea9d1..fd126ce234c 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -24,7 +24,7 @@ jobs: - '.github/workflows/solidity-foundry.yml' - 'contracts/foundry.toml' - 'contracts/gas-snapshots/*.gas-snapshot' - - 'contracts/foundry-lib' + - 'contracts/foundry-lib/**/*' - '.gitmodules' tests: diff --git a/contracts/foundry-lib/forge-std b/contracts/foundry-lib/forge-std index f73c73d2018..978ac6fadb6 160000 --- a/contracts/foundry-lib/forge-std +++ b/contracts/foundry-lib/forge-std @@ -1 +1 @@ -Subproject commit f73c73d2018eb6a111f35e4dae7b4f27401e9421 +Subproject commit 978ac6fadb62f5f0b723c996f64be52eddba6801 diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index c7bede2770d..53b34c786c9 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -30,7 +30,7 @@ FunctionsBilling_UpdateConfig:test_UpdateConfig_Success() (gas: 53173) FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8810) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_RevertIfInvalidCommitment() (gas: 13375) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_Success() (gas: 186305) -FunctionsBilling__StartBilling:test__FulfillAndBill_HasUniqueGlobalRequestId() (gas: 524682) +FunctionsBilling__StartBilling:test__FulfillAndBill_HasUniqueGlobalRequestId() (gas: 524688) FunctionsClient_Constructor:test_Constructor_Success() (gas: 10407) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_RevertIfNotRouter() (gas: 14617) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_Success() (gas: 22917) @@ -167,7 +167,7 @@ FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessIfRecipientAddres FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessPaysRecipient() (gas: 33935) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessSetsBalanceToZero() (gas: 31584) FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessFalseIfNoPendingRequests() (gas: 17818) -FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessTrueIfPendingRequests() (gas: 203439) +FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessTrueIfPendingRequests() (gas: 203449) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfEmptyNewOwner() (gas: 27664) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfInvalidNewOwner() (gas: 57815) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNoSubscription() (gas: 15013) diff --git a/contracts/gas-snapshots/keystone.gas-snapshot b/contracts/gas-snapshots/keystone.gas-snapshot new file mode 100644 index 00000000000..51970c046bd --- /dev/null +++ b/contracts/gas-snapshots/keystone.gas-snapshot @@ -0,0 +1,65 @@ +CapabilityRegistry_AddCapabilityTest:test_AddCapability_NoConfigurationContract() (gas: 136922) +CapabilityRegistry_AddCapabilityTest:test_AddCapability_WithConfiguration() (gas: 160688) +CapabilityRegistry_AddCapabilityTest:test_DeprecatesCapability() (gas: 87158) +CapabilityRegistry_AddCapabilityTest:test_EmitsEvent() (gas: 87332) +CapabilityRegistry_AddCapabilityTest:test_RevertWhen_CalledByNonAdmin() (gas: 22371) +CapabilityRegistry_AddCapabilityTest:test_RevertWhen_CalledByNonAdmin() (gas: 23590) +CapabilityRegistry_AddCapabilityTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 15513) +CapabilityRegistry_AddCapabilityTest:test_RevertWhen_CapabilityExists() (gas: 133959) +CapabilityRegistry_AddCapabilityTest:test_RevertWhen_CapabilityIsDeprecated() (gas: 88022) +CapabilityRegistry_AddCapabilityTest:test_RevertWhen_ConfigurationContractDoesNotMatchInterface() (gas: 2725761) +CapabilityRegistry_AddCapabilityTest:test_RevertWhen_ConfigurationContractNotDeployed() (gas: 23513) +CapabilityRegistry_AddDONTest:test_AddDON() (gas: 227135) +CapabilityRegistry_AddDONTest:test_RevertWhen_CalledByNonAdmin() (gas: 21160) +CapabilityRegistry_AddDONTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 89373) +CapabilityRegistry_AddDONTest:test_RevertWhen_DeprecatedCapabilityAdded() (gas: 180895) +CapabilityRegistry_AddDONTest:test_RevertWhen_DuplicateCapabilityAdded() (gas: 210428) +CapabilityRegistry_AddDONTest:test_RevertWhen_DuplicateNodeAdded() (gas: 109784) +CapabilityRegistry_AddDONTest:test_RevertWhen_NodeDoesNotSupportCapability() (gas: 120851) +CapabilityRegistry_AddNodeOperatorsTest:test_AddNodeOperators() (gas: 164127) +CapabilityRegistry_AddNodeOperatorsTest:test_RevertWhen_CalledByNonAdmin() (gas: 28654) +CapabilityRegistry_AddNodeOperatorsTest:test_RevertWhen_NodeOperatorAdminAddressZero() (gas: 29520) +CapabilityRegistry_AddNodesTest:test_AddsNodeParams() (gas: 205953) +CapabilityRegistry_AddNodesTest:test_OwnerCanAddNodes() (gas: 205953) +CapabilityRegistry_AddNodesTest:test_RevertWhen_AddingDuplicateP2PId() (gas: 152430) +CapabilityRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 57467) +CapabilityRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithoutCapabilities() (gas: 31747) +CapabilityRegistry_AddNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 30923) +CapabilityRegistry_AddNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 31612) +CapabilityRegistry_AddNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 31449) +CapabilityRegistry_GetCapabilitiesTest:test_ExcludesDeprecatedCapabilities() (gas: 109143) +CapabilityRegistry_GetCapabilitiesTest:test_ReturnsCapabilities() (gas: 44169) +CapabilityRegistry_RemoveNodeOperatorsTest:test_RemovesNodeOperator() (gas: 36837) +CapabilityRegistry_RemoveNodeOperatorsTest:test_RevertWhen_CalledByNonOwner() (gas: 17912) +CapabilityRegistry_RemoveNodesTest:test_OwnerCanRemoveNodes() (gas: 35545) +CapabilityRegistry_RemoveNodesTest:test_RemovesNode() (gas: 35556) +CapabilityRegistry_RemoveNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 27298) +CapabilityRegistry_RemoveNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 20508) +CapabilityRegistry_RemoveNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 20498) +CapabilityRegistry_UpdateNodeOperatorTest:test_RevertWhen_CalledByNonAdminAndNonOwner() (gas: 19819) +CapabilityRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorAdminIsZeroAddress() (gas: 19754) +CapabilityRegistry_UpdateNodeOperatorTest:test_UpdatesNodeOperator() (gas: 38439) +CapabilityRegistry_UpdateNodesTest:test_OwnerCanUpdateNodes() (gas: 119396) +CapabilityRegistry_UpdateNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 40310) +CapabilityRegistry_UpdateNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 30901) +CapabilityRegistry_UpdateNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 31576) +CapabilityRegistry_UpdateNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 31575) +CapabilityRegistry_UpdateNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 31390) +CapabilityRegistry_UpdateNodesTest:test_RevertWhen_UpdatingNodeWithoutCapabilities() (gas: 31667) +CapabilityRegistry_UpdateNodesTest:test_UpdatesNodeParams() (gas: 119439) +KeystoneForwarder_ReportTest:test_Report_SuccessfulDelivery() (gas: 133714) +KeystoneForwarder_ReportTest:test_RevertWhen_AnySignatureIsInvalid() (gas: 97790) +KeystoneForwarder_ReportTest:test_RevertWhen_AnySignerIsInvalid() (gas: 110159) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportAlreadyProcessed() (gas: 141632) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportHasDuplicateSignatures() (gas: 110451) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportHasIncorrectDON() (gas: 84208) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportIsMalformed() (gas: 58504) +KeystoneForwarder_ReportTest:test_RevertWhen_TooFewSignatures() (gas: 68813) +KeystoneForwarder_ReportTest:test_RevertWhen_TooManySignatures() (gas: 69493) +KeystoneForwarder_SetConfigTest:test_RevertWhen_ExcessSigners() (gas: 40038) +KeystoneForwarder_SetConfigTest:test_RevertWhen_FaultToleranceIsZero() (gas: 109995) +KeystoneForwarder_SetConfigTest:test_RevertWhen_InsufficientSigners() (gas: 34399) +KeystoneForwarder_SetConfigTest:test_RevertWhen_ProvidingDuplicateSigners() (gas: 867400) +KeystoneForwarder_SetConfigTest:test_SetConfig_FirstTime() (gas: 2224742) +KeystoneForwarder_SetConfigTest:test_SetConfig_WhenSignersAreRemoved() (gas: 2399768) +KeystoneForwarder_TypeAndVersionTest:test_TypeAndVersion() (gas: 9641) \ No newline at end of file diff --git a/contracts/gas-snapshots/operatorforwarder.gas-snapshot b/contracts/gas-snapshots/operatorforwarder.gas-snapshot index ee6c063f2f3..7cfc963f74e 100644 --- a/contracts/gas-snapshots/operatorforwarder.gas-snapshot +++ b/contracts/gas-snapshots/operatorforwarder.gas-snapshot @@ -8,14 +8,8 @@ ForwarderTest:test_OwnerForward_Success() (gas: 30118) ForwarderTest:test_SetAuthorizedSenders_Success() (gas: 160524) ForwarderTest:test_TransferOwnershipWithMessage_Success() (gas: 35123) OperatorTest:test_CancelOracleRequest_Success() (gas: 274436) -OperatorTest:test_CancelOracleRequest_Success() (gas: 274436) -OperatorTest:test_FulfillOracleRequest_Success() (gas: 330603) OperatorTest:test_FulfillOracleRequest_Success() (gas: 330603) OperatorTest:test_NotAuthorizedSender_Revert() (gas: 246716) -OperatorTest:test_NotAuthorizedSender_Revert() (gas: 246716) OperatorTest:test_OracleRequest_Success() (gas: 250019) -OperatorTest:test_OracleRequest_Success() (gas: 250019) -OperatorTest:test_SendRequestAndCancelRequest_Success(uint96) (runs: 256, μ: 387120, ~: 387124) OperatorTest:test_SendRequestAndCancelRequest_Success(uint96) (runs: 256, μ: 387120, ~: 387124) -OperatorTest:test_SendRequest_Success(uint96) (runs: 256, μ: 303611, ~: 303620) -OperatorTest:test_SendRequest_Success(uint96) (runs: 256, μ: 303611, ~: 303620) \ No newline at end of file +OperatorTest:test_SendRequest_Success(uint96) (runs: 256, μ: 303611, ~: 303615) \ No newline at end of file diff --git a/contracts/gas-snapshots/shared.gas-snapshot b/contracts/gas-snapshots/shared.gas-snapshot index c41c633749c..0a799549c9e 100644 --- a/contracts/gas-snapshots/shared.gas-snapshot +++ b/contracts/gas-snapshots/shared.gas-snapshot @@ -30,10 +30,10 @@ CallWithExactGas__callWithExactGas:test_CallWithExactGasSafeReturnDataExactGas() CallWithExactGas__callWithExactGas:test_NoContractReverts() (gas: 11559) CallWithExactGas__callWithExactGas:test_NoGasForCallExactCheckReverts() (gas: 15788) CallWithExactGas__callWithExactGas:test_NotEnoughGasForCallReverts() (gas: 16241) -CallWithExactGas__callWithExactGas:test_callWithExactGasSuccess(bytes,bytes4) (runs: 256, μ: 15812, ~: 15752) +CallWithExactGas__callWithExactGas:test_callWithExactGasSuccess(bytes,bytes4) (runs: 256, μ: 15811, ~: 15752) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractExactGasSuccess() (gas: 20116) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractReceiverErrorSuccess() (gas: 67721) -CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractSuccess(bytes,bytes4) (runs: 256, μ: 16322, ~: 16262) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractSuccess(bytes,bytes4) (runs: 256, μ: 16321, ~: 16262) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NoContractSuccess() (gas: 12962) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NoGasForCallExactCheckReturnFalseSuccess() (gas: 13005) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NotEnoughGasForCallReturnsFalseSuccess() (gas: 13317) From 5c367205173ad4310ae289ad897e5b1f7862a71d Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Mon, 27 May 2024 14:41:32 +0200 Subject: [PATCH 07/19] Increase HTTPWriteTimeout in E2E tests (#13318) --- integration-tests/testconfig/automation/automation.toml | 2 +- integration-tests/testconfig/automation/example.toml | 2 +- integration-tests/testconfig/default.toml | 2 +- integration-tests/testconfig/forwarder_ocr/example.toml | 2 +- integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml | 2 +- integration-tests/testconfig/forwarder_ocr2/example.toml | 2 +- integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml | 2 +- integration-tests/testconfig/functions/example.toml | 2 +- integration-tests/testconfig/keeper/example.toml | 2 +- integration-tests/testconfig/keeper/keeper.toml | 2 +- integration-tests/testconfig/log_poller/example.toml | 2 +- integration-tests/testconfig/log_poller/log_poller.toml | 2 +- integration-tests/testconfig/ocr/example.toml | 2 +- integration-tests/testconfig/ocr/ocr.toml | 2 +- integration-tests/testconfig/ocr2/example.toml | 2 +- integration-tests/testconfig/ocr2/ocr2.toml | 2 +- integration-tests/testconfig/vrf/vrf.toml | 2 +- integration-tests/testconfig/vrfv2/example.toml | 2 +- integration-tests/testconfig/vrfv2plus/example.toml | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/integration-tests/testconfig/automation/automation.toml b/integration-tests/testconfig/automation/automation.toml index 05195e2391d..61203758148 100644 --- a/integration-tests/testconfig/automation/automation.toml +++ b/integration-tests/testconfig/automation/automation.toml @@ -20,7 +20,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/automation/example.toml b/integration-tests/testconfig/automation/example.toml index 56d84cceeef..cc48749a15b 100644 --- a/integration-tests/testconfig/automation/example.toml +++ b/integration-tests/testconfig/automation/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index 0c8d411c04a..f937faf9c24 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -47,7 +47,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/forwarder_ocr/example.toml b/integration-tests/testconfig/forwarder_ocr/example.toml index 469b765b1db..314c1b6781c 100644 --- a/integration-tests/testconfig/forwarder_ocr/example.toml +++ b/integration-tests/testconfig/forwarder_ocr/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml b/integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml index f38cb4f5c4b..79e6763e24f 100644 --- a/integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml +++ b/integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml @@ -16,7 +16,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/forwarder_ocr2/example.toml b/integration-tests/testconfig/forwarder_ocr2/example.toml index 2bd03827506..9f99284cc29 100644 --- a/integration-tests/testconfig/forwarder_ocr2/example.toml +++ b/integration-tests/testconfig/forwarder_ocr2/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml b/integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml index 7ce658935f8..f95b56f17cf 100644 --- a/integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml +++ b/integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml @@ -16,7 +16,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/functions/example.toml b/integration-tests/testconfig/functions/example.toml index 7cd875d4c8d..77420ceaf30 100644 --- a/integration-tests/testconfig/functions/example.toml +++ b/integration-tests/testconfig/functions/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/keeper/example.toml b/integration-tests/testconfig/keeper/example.toml index 87cf2045feb..c9a5c829d38 100644 --- a/integration-tests/testconfig/keeper/example.toml +++ b/integration-tests/testconfig/keeper/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/keeper/keeper.toml b/integration-tests/testconfig/keeper/keeper.toml index b3b0869e7c5..9b2a1a65891 100644 --- a/integration-tests/testconfig/keeper/keeper.toml +++ b/integration-tests/testconfig/keeper/keeper.toml @@ -20,7 +20,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/log_poller/example.toml b/integration-tests/testconfig/log_poller/example.toml index a41433b1d06..69d1263b94e 100644 --- a/integration-tests/testconfig/log_poller/example.toml +++ b/integration-tests/testconfig/log_poller/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/log_poller/log_poller.toml b/integration-tests/testconfig/log_poller/log_poller.toml index 2f34bf3863d..6b088054479 100644 --- a/integration-tests/testconfig/log_poller/log_poller.toml +++ b/integration-tests/testconfig/log_poller/log_poller.toml @@ -24,7 +24,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/ocr/example.toml b/integration-tests/testconfig/ocr/example.toml index 3355ede75f7..3f449566760 100644 --- a/integration-tests/testconfig/ocr/example.toml +++ b/integration-tests/testconfig/ocr/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/ocr/ocr.toml b/integration-tests/testconfig/ocr/ocr.toml index 5bc9b975648..31224599d8d 100644 --- a/integration-tests/testconfig/ocr/ocr.toml +++ b/integration-tests/testconfig/ocr/ocr.toml @@ -20,7 +20,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/ocr2/example.toml b/integration-tests/testconfig/ocr2/example.toml index 9f7fdc7ff92..6e7138aeda3 100644 --- a/integration-tests/testconfig/ocr2/example.toml +++ b/integration-tests/testconfig/ocr2/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/ocr2/ocr2.toml b/integration-tests/testconfig/ocr2/ocr2.toml index 1d33cdc1ec4..856dd73beb3 100644 --- a/integration-tests/testconfig/ocr2/ocr2.toml +++ b/integration-tests/testconfig/ocr2/ocr2.toml @@ -20,7 +20,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/vrf/vrf.toml b/integration-tests/testconfig/vrf/vrf.toml index 5282dae8910..a8b5f19f6b1 100644 --- a/integration-tests/testconfig/vrf/vrf.toml +++ b/integration-tests/testconfig/vrf/vrf.toml @@ -16,7 +16,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/vrfv2/example.toml b/integration-tests/testconfig/vrfv2/example.toml index 0351ae85142..7b15597d59a 100644 --- a/integration-tests/testconfig/vrfv2/example.toml +++ b/integration-tests/testconfig/vrfv2/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] diff --git a/integration-tests/testconfig/vrfv2plus/example.toml b/integration-tests/testconfig/vrfv2plus/example.toml index 1aaf8951e20..7931b879084 100644 --- a/integration-tests/testconfig/vrfv2plus/example.toml +++ b/integration-tests/testconfig/vrfv2plus/example.toml @@ -92,7 +92,7 @@ MaxSize = '0b' AllowOrigins = '*' HTTPPort = 6688 SecureCookies = false -HTTPWriteTimeout = '1m' +HTTPWriteTimeout = '3m' SessionTimeout = '999h0m0s' [WebServer.RateLimit] From a85d828fde24248fc2e4d6fc27329879d471accc Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Mon, 27 May 2024 14:49:50 +0200 Subject: [PATCH 08/19] Run E2E smoke tests on DB migrations (#13324) --- .github/workflows/integration-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 9e944ead9bf..038fec0f66c 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -98,6 +98,7 @@ jobs: - '**/*go.mod' - '.github/workflows/integration-tests.yml' - '**/*Dockerfile' + - 'core/**/migrations/*.sql' - 'core/**/config/**/*.toml' - 'integration-tests/**/*.toml' - name: Ignore Filter On Workflow Dispatch From 33dfd57ccc3a205cf8c00cd165347fcf0a78c899 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Mon, 27 May 2024 15:31:33 +0200 Subject: [PATCH 09/19] uncomment Besu in compatibility pipeline (#13332) --- .../workflows/client-compatibility-tests.yml | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml index f69be85f896..e50d64f3dac 100644 --- a/.github/workflows/client-compatibility-tests.yml +++ b/.github/workflows/client-compatibility-tests.yml @@ -111,13 +111,12 @@ jobs: client: nethermind timeout: 30m pyroscope_env: ci-smoke-ocr-nethermind-simulated - # Will fail until https://github.com/hyperledger/besu/pull/6702 is merged and released - # - name: ocr-besu - # test: TestOCRBasic - # file: ocr - # client: besu - # timeout: 30m - # pyroscope_env: ci-smoke-ocr-besu-simulated + - name: ocr-besu + test: TestOCRBasic + file: ocr + client: besu + timeout: 30m + pyroscope_env: ci-smoke-ocr-besu-simulated - name: ocr-erigon test: TestOCRBasic file: ocr @@ -136,13 +135,12 @@ jobs: client: nethermind timeout: 30m pyroscope_env: ci-smoke-nethermind-evm-simulated - # Will fail until https://github.com/hyperledger/besu/pull/6702 is merged and released - # - name: ocr2-besu - # test: "^TestOCRv2Basic/plugins$" - # file: ocr2 - # client: besu - # timeout: 30m - # pyroscope_env: ci-smoke-ocr2-besu-simulated + - name: ocr2-besu + test: "^TestOCRv2Basic/plugins$" + file: ocr2 + client: besu + timeout: 30m + pyroscope_env: ci-smoke-ocr2-besu-simulated - name: ocr2-erigon test: "^TestOCRv2Basic/plugins$" file: ocr2 From d675d864f0e6f33c783bfed17fe31b2c127eb51d Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Mon, 27 May 2024 10:34:47 -0500 Subject: [PATCH 10/19] Add feature to TXM to detect and purge stuck transactions (#12881) * Added the EVM stuck tx detector component * Added stuck tx handling in Confirmer * Fixed linting * Fixed toml config decoding * Fixed config tests * Fixed web resolver config tests * Fixed config docs test * Added zkEVM overflow detection and added unit tests for the detector * Fixed broken tests after merge * Reverted AutoPurgeConfig validation changes and fixed config tests * Added changeset and fixed config validation logic * Fixed linting * Fixed linting * Fixed purge attempt builder and added tests * Updated evm.txes contraint to allow non-null nonce if fatal_error * Added confirmer test * Adjusted confirmer test to better reflect actual process * Fixed linting * Added purge block num loading on Confirmer startup * Updated EVM tx store mock * Fixed linting and testdata * Updated stuck tx fatal error messages * Updated sql migration file sequence * Skipped loading purge block num if auto-purge feature disabled and fixed test * Fixed linting * Renamed function and moved log * Added stricter config validation * Fixed linting * Updated auto-purge feature configs to adhere to config naming standards * Fixed config doc test and updated changeset * Updated Scroll and zkEVM config defaults and linted common config * Updated config doc * Generated config doc and fixed linting * Updated config description for AutoPurge.MinAttempts * Fixed sql migration conflict * Fixed linting * Updated stuck tx detector to use PriceMax config and added comments * Fixed linting * Updated DetectionApiUrl config example * Fixed issues from latest merge * Renumbered sql migration file * Fixed testdata --- .changeset/red-eagles-cry.md | 5 + common/config/chaintype.go | 6 +- common/txmgr/confirmer.go | 106 ++++- .../txmgr/types/mocks/tx_attempt_builder.go | 28 ++ common/txmgr/types/mocks/tx_store.go | 10 +- common/txmgr/types/stuck_tx_detector.go | 26 ++ common/txmgr/types/tx.go | 1 + common/txmgr/types/tx_attempt_builder.go | 3 + common/txmgr/types/tx_store.go | 2 +- .../evm/config/chain_scoped_transactions.go | 25 + core/chains/evm/config/config.go | 9 + core/chains/evm/config/toml/config.go | 63 +++ .../toml/defaults/Polygon_Zkevm_Cardona.toml | 1 + .../toml/defaults/Polygon_Zkevm_Goerli.toml | 1 + .../toml/defaults/Polygon_Zkevm_Mainnet.toml | 1 + .../evm/config/toml/defaults/fallback.toml | 3 + core/chains/evm/txmgr/attempts.go | 33 +- core/chains/evm/txmgr/attempts_test.go | 83 ++++ core/chains/evm/txmgr/builder.go | 6 +- core/chains/evm/txmgr/confirmer_test.go | 113 ++++- core/chains/evm/txmgr/evm_tx_store.go | 48 +- core/chains/evm/txmgr/evm_tx_store_test.go | 6 +- core/chains/evm/txmgr/mocks/evm_tx_store.go | 40 +- core/chains/evm/txmgr/models.go | 1 + core/chains/evm/txmgr/stuck_tx_detector.go | 375 +++++++++++++++ .../evm/txmgr/stuck_tx_detector_test.go | 433 ++++++++++++++++++ core/chains/evm/txmgr/test_helpers.go | 31 +- core/chains/evm/txmgr/txmgr_test.go | 2 +- core/cmd/shell_local.go | 3 +- core/config/docs/chains-evm.toml | 10 + core/config/docs/docs_test.go | 5 + core/services/chainlink/config_test.go | 27 +- .../chainlink/testdata/config-full.toml | 3 + .../chainlink/testdata/config-invalid.toml | 16 + .../config-multi-chain-effective.toml | 9 + core/services/ocr/contract_tracker.go | 2 +- core/services/ocrcommon/block_translator.go | 2 +- .../0239_add_purge_column_tx_attempts.sql | 32 ++ core/web/resolver/testdata/config-full.toml | 3 + .../config-multi-chain-effective.toml | 9 + docs/CONFIG.md | 214 +++++++++ .../disk-based-logging-disabled.txtar | 3 + .../validate/disk-based-logging-no-dir.txtar | 3 + .../node/validate/disk-based-logging.txtar | 3 + testdata/scripts/node/validate/invalid.txtar | 3 + testdata/scripts/node/validate/valid.txtar | 3 + testdata/scripts/node/validate/warnings.txtar | 3 + 47 files changed, 1761 insertions(+), 53 deletions(-) create mode 100644 .changeset/red-eagles-cry.md create mode 100644 common/txmgr/types/stuck_tx_detector.go create mode 100644 core/chains/evm/txmgr/stuck_tx_detector.go create mode 100644 core/chains/evm/txmgr/stuck_tx_detector_test.go create mode 100644 core/store/migrate/migrations/0239_add_purge_column_tx_attempts.sql diff --git a/.changeset/red-eagles-cry.md b/.changeset/red-eagles-cry.md new file mode 100644 index 00000000000..3474435911d --- /dev/null +++ b/.changeset/red-eagles-cry.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#added Added an auto-purge feature to the EVM TXM that identifies terminally stuck transactions either through a chain specific method or heurisitic then purges them to unblock the nonce. Included 4 new toml configs under Transactions.AutoPurge to configure this new feature: Enabled, Threshold, MinAttempts, and DetectionApiUrl. diff --git a/common/config/chaintype.go b/common/config/chaintype.go index 3f3150950d6..1ddb3f626b5 100644 --- a/common/config/chaintype.go +++ b/common/config/chaintype.go @@ -17,6 +17,7 @@ const ( ChainScroll ChainType = "scroll" ChainWeMix ChainType = "wemix" ChainXLayer ChainType = "xlayer" + ChainZkEvm ChainType = "zkevm" ChainZkSync ChainType = "zksync" ) @@ -34,7 +35,7 @@ func (c ChainType) IsL2() bool { func (c ChainType) IsValid() bool { switch c { - case "", ChainArbitrum, ChainCelo, ChainGnosis, ChainKroma, ChainMetis, ChainOptimismBedrock, ChainScroll, ChainWeMix, ChainXLayer, ChainZkSync: + case "", ChainArbitrum, ChainCelo, ChainGnosis, ChainKroma, ChainMetis, ChainOptimismBedrock, ChainScroll, ChainWeMix, ChainXLayer, ChainZkEvm, ChainZkSync: return true } return false @@ -60,6 +61,8 @@ func ChainTypeFromSlug(slug string) ChainType { return ChainWeMix case "xlayer": return ChainXLayer + case "zkevm": + return ChainZkEvm case "zksync": return ChainZkSync default: @@ -123,5 +126,6 @@ var ErrInvalidChainType = fmt.Errorf("must be one of %s or omitted", strings.Joi string(ChainScroll), string(ChainWeMix), string(ChainXLayer), + string(ChainZkEvm), string(ChainZkSync), }, ", ")) diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 30fbbc48987..294e922c1c0 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -122,12 +122,13 @@ type Confirmer[ lggr logger.SugaredLogger client txmgrtypes.TxmClient[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] - resumeCallback ResumeCallback - chainConfig txmgrtypes.ConfirmerChainConfig - feeConfig txmgrtypes.ConfirmerFeeConfig - txConfig txmgrtypes.ConfirmerTransactionsConfig - dbConfig txmgrtypes.ConfirmerDatabaseConfig - chainID CHAIN_ID + stuckTxDetector txmgrtypes.StuckTxDetector[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + resumeCallback ResumeCallback + chainConfig txmgrtypes.ConfirmerChainConfig + feeConfig txmgrtypes.ConfirmerFeeConfig + txConfig txmgrtypes.ConfirmerTransactionsConfig + dbConfig txmgrtypes.ConfirmerDatabaseConfig + chainID CHAIN_ID ks txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ] enabledAddresses []ADDR @@ -162,6 +163,7 @@ func NewConfirmer[ txAttemptBuilder txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], lggr logger.Logger, isReceiptNil func(R) bool, + stuckTxDetector txmgrtypes.StuckTxDetector[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], ) *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { lggr = logger.Named(lggr, "Confirmer") return &Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ @@ -178,6 +180,7 @@ func NewConfirmer[ ks: keystore, mb: mailbox.NewSingle[HEAD](), isReceiptNil: isReceiptNil, + stuckTxDetector: stuckTxDetector, } } @@ -205,6 +208,9 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) sta if err != nil { return fmt.Errorf("Confirmer: failed to load EnabledAddressesForChain: %w", err) } + if err = ec.stuckTxDetector.LoadPurgeBlockNumMap(ctx, ec.enabledAddresses); err != nil { + ec.lggr.Debugf("Confirmer: failed to load the last purged block num for enabled addresses. Process can continue as normal but purge rate limiting may be affected.") + } ec.stopCh = make(chan struct{}) ec.wg = sync.WaitGroup{} @@ -298,6 +304,13 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) pro ec.lggr.Debugw("Finished CheckForReceipts", "headNum", head.BlockNumber(), "time", time.Since(mark), "id", "confirmer") mark = time.Now() + if err := ec.ProcessStuckTransactions(ctx, head.BlockNumber()); err != nil { + return fmt.Errorf("ProcessStuckTransactions failed: %w", err) + } + + ec.lggr.Debugw("Finished ProcessStuckTransactions", "headNum", head.BlockNumber(), "time", time.Since(mark), "id", "confirmer") + mark = time.Now() + if err := ec.RebroadcastWhereNecessary(ctx, head.BlockNumber()); err != nil { return fmt.Errorf("RebroadcastWhereNecessary failed: %w", err) } @@ -436,6 +449,57 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Che return nil } +// Determines if any of the unconfirmed transactions are terminally stuck for each enabled address +// If any transaction is found to be terminally stuck, this method sends an empty attempt with bumped gas in an attempt to purge the stuck transaction +func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ProcessStuckTransactions(ctx context.Context, blockNum int64) error { + // Use the detector to find a stuck tx for each enabled address + stuckTxs, err := ec.stuckTxDetector.DetectStuckTransactions(ctx, ec.enabledAddresses, blockNum) + if err != nil { + return fmt.Errorf("failed to detect stuck transactions: %w", err) + } + if len(stuckTxs) == 0 { + return nil + } + + var wg sync.WaitGroup + wg.Add(len(stuckTxs)) + errorList := []error{} + var errMu sync.Mutex + for _, tx := range stuckTxs { + // All stuck transactions will have unique from addresses. It is safe to process separate keys concurrently + // NOTE: This design will block one key if another takes a really long time to execute + go func(tx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) { + defer wg.Done() + lggr := tx.GetLogger(ec.lggr) + // Create an purge attempt for tx + purgeAttempt, err := ec.TxAttemptBuilder.NewPurgeTxAttempt(ctx, tx, lggr) + if err != nil { + errMu.Lock() + errorList = append(errorList, fmt.Errorf("failed to create a purge attempt: %w", err)) + errMu.Unlock() + return + } + // Save purge attempt + if err := ec.txStore.SaveInProgressAttempt(ctx, &purgeAttempt); err != nil { + errMu.Lock() + errorList = append(errorList, fmt.Errorf("failed to save purge attempt: %w", err)) + errMu.Unlock() + return + } + lggr.Warnw("marked transaction as terminally stuck", "etx", tx) + // Send purge attempt + if err := ec.handleInProgressAttempt(ctx, lggr, tx, purgeAttempt, blockNum); err != nil { + errMu.Lock() + errorList = append(errorList, fmt.Errorf("failed to send purge attempt: %w", err)) + errMu.Unlock() + return + } + }(tx) + } + wg.Wait() + return errors.Join(errorList...) +} + func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) separateLikelyConfirmedAttempts(from ADDR, attempts []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], minedSequence SEQ) []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { if len(attempts) == 0 { return attempts @@ -486,7 +550,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) fet j = len(attempts) } - ec.lggr.Debugw(fmt.Sprintf("Batch fetching receipts at indexes %v until (excluded) %v", i, j), "blockNum", blockNum) + ec.lggr.Debugw(fmt.Sprintf("Batch fetching receipts at indexes %d until (excluded) %d", i, j), "blockNum", blockNum) batch := attempts[i:j] @@ -494,7 +558,13 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) fet if err != nil { return fmt.Errorf("batchFetchReceipts failed: %w", err) } - if err := ec.txStore.SaveFetchedReceipts(ctx, receipts, ec.chainID); err != nil { + validReceipts, purgeReceipts := ec.separateValidAndPurgeAttemptReceipts(receipts, batch) + // Saves the receipts and mark the associated transactions as Confirmed + if err := ec.txStore.SaveFetchedReceipts(ctx, validReceipts, TxConfirmed, nil, ec.chainID); err != nil { + return fmt.Errorf("saveFetchedReceipts failed: %w", err) + } + // Save the receipts but mark the associated transactions as Fatal Error since the original transaction was purged + if err := ec.txStore.SaveFetchedReceipts(ctx, purgeReceipts, TxFatalError, ec.stuckTxDetector.StuckTxFatalError(), ec.chainID); err != nil { return fmt.Errorf("saveFetchedReceipts failed: %w", err) } promNumConfirmedTxs.WithLabelValues(ec.chainID.String()).Add(float64(len(receipts))) @@ -507,6 +577,25 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) fet return nil } +func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) separateValidAndPurgeAttemptReceipts(receipts []R, attempts []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) (valid []R, purge []R) { + receiptMap := make(map[TX_HASH]R) + for _, receipt := range receipts { + receiptMap[receipt.GetTxHash()] = receipt + } + for _, attempt := range attempts { + if receipt, ok := receiptMap[attempt.Hash]; ok { + if attempt.IsPurgeAttempt { + // Setting the purged block num here is ok since we have confirmation the tx has been purged with the receipt + ec.stuckTxDetector.SetPurgeBlockNum(attempt.Tx.FromAddress, receipt.GetBlockNumber().Int64()) + purge = append(purge, receipt) + } else { + valid = append(valid, receipt) + } + } + } + return +} + func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) getMinedSequenceForAddress(ctx context.Context, from ADDR) (SEQ, error) { return ec.client.SequenceAt(ctx, from, nil) } @@ -700,7 +789,6 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Fin lggr.Infow(fmt.Sprintf("Found %d transactions to be re-sent that were previously rejected due to insufficient native token balance", len(etxInsufficientFunds)), "blockNum", blockNum, "address", address) } - // TODO: Just pass the Q through everything etxBumps, err := ec.txStore.FindTxsRequiringGasBump(ctx, address, blockNum, gasBumpThreshold, bumpDepth, chainID) if ctx.Err() != nil { return nil, nil diff --git a/common/txmgr/types/mocks/tx_attempt_builder.go b/common/txmgr/types/mocks/tx_attempt_builder.go index 8171b29bbed..47614674336 100644 --- a/common/txmgr/types/mocks/tx_attempt_builder.go +++ b/common/txmgr/types/mocks/tx_attempt_builder.go @@ -188,6 +188,34 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) return r0, r1 } +// NewPurgeTxAttempt provides a mock function with given fields: ctx, etx, lggr +func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) NewPurgeTxAttempt(ctx context.Context, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], lggr logger.Logger) (txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, etx, lggr) + + if len(ret) == 0 { + panic("no return value specified for NewPurgeTxAttempt") + } + + var r0 txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger) (txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, etx, lggr) + } + if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger) txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, etx, lggr) + } else { + r0 = ret.Get(0).(txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + + if rf, ok := ret.Get(1).(func(context.Context, txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], logger.Logger) error); ok { + r1 = rf(ctx, etx, lggr) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewTxAttempt provides a mock function with given fields: ctx, tx, lggr, opts func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) NewTxAttempt(ctx context.Context, tx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], lggr logger.Logger, opts ...feetypes.Opt) (txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], FEE, uint64, bool, error) { _va := make([]interface{}, len(opts)) diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index be2c0aef723..bb8fabf1a71 100644 --- a/common/txmgr/types/mocks/tx_store.go +++ b/common/txmgr/types/mocks/tx_store.go @@ -1014,17 +1014,17 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveConfirm return r0 } -// SaveFetchedReceipts provides a mock function with given fields: ctx, receipts, chainID -func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveFetchedReceipts(ctx context.Context, receipts []R, chainID CHAIN_ID) error { - ret := _m.Called(ctx, receipts, chainID) +// SaveFetchedReceipts provides a mock function with given fields: ctx, r, state, errorMsg, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SaveFetchedReceipts(ctx context.Context, r []R, state txmgrtypes.TxState, errorMsg *string, chainID CHAIN_ID) error { + ret := _m.Called(ctx, r, state, errorMsg, chainID) if len(ret) == 0 { panic("no return value specified for SaveFetchedReceipts") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []R, CHAIN_ID) error); ok { - r0 = rf(ctx, receipts, chainID) + if rf, ok := ret.Get(0).(func(context.Context, []R, txmgrtypes.TxState, *string, CHAIN_ID) error); ok { + r0 = rf(ctx, r, state, errorMsg, chainID) } else { r0 = ret.Error(0) } diff --git a/common/txmgr/types/stuck_tx_detector.go b/common/txmgr/types/stuck_tx_detector.go new file mode 100644 index 00000000000..c4ca94b87f8 --- /dev/null +++ b/common/txmgr/types/stuck_tx_detector.go @@ -0,0 +1,26 @@ +package types + +import ( + "context" + + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +// StuckTxDetector is used by the Confirmer to determine if any unconfirmed transactions are terminally stuck +type StuckTxDetector[ + CHAIN_ID types.ID, // CHAIN_ID - chain id type + ADDR types.Hashable, // ADDR - chain address type + TX_HASH, BLOCK_HASH types.Hashable, // various chain hash types + SEQ types.Sequence, // SEQ - chain sequence type (nonce, utxo, etc) + FEE feetypes.Fee, // FEE - chain fee type +] interface { + // Uses either a chain specific API or heuristic to determine if any unconfirmed transactions are terminally stuck. Returns only one transaction per enabled address. + DetectStuckTransactions(ctx context.Context, enabledAddresses []ADDR, blockNum int64) ([]Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) + // Loads the internal map that tracks the last block num a transaction was purged at using the DB state + LoadPurgeBlockNumMap(ctx context.Context, addresses []ADDR) error + // Sets the last purged block num after a transaction has been successfully purged with receipt + SetPurgeBlockNum(fromAddress ADDR, blockNum int64) + // Returns the error message to set in the transaction error field to mark it as terminally stuck + StuckTxFatalError() *string +} diff --git a/common/txmgr/types/tx.go b/common/txmgr/types/tx.go index 76fd7f4b3ab..e04ebe95871 100644 --- a/common/txmgr/types/tx.go +++ b/common/txmgr/types/tx.go @@ -183,6 +183,7 @@ type TxAttempt[ State TxAttemptState Receipts []ChainReceipt[TX_HASH, BLOCK_HASH] `json:"-"` TxType int + IsPurgeAttempt bool } func (a *TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) String() string { diff --git a/common/txmgr/types/tx_attempt_builder.go b/common/txmgr/types/tx_attempt_builder.go index 54184733f0a..a9cbddfc2cf 100644 --- a/common/txmgr/types/tx_attempt_builder.go +++ b/common/txmgr/types/tx_attempt_builder.go @@ -42,4 +42,7 @@ type TxAttemptBuilder[ // NewEmptyTxAttempt is used in ForceRebroadcast to create a signed tx with zero value sent to the zero address NewEmptyTxAttempt(ctx context.Context, seq SEQ, feeLimit uint64, fee FEE, fromAddress ADDR) (attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + + // NewPurgeTxAttempt is used to create empty transaction attempts with higher gas than the previous attempt to purge stuck transactions + NewPurgeTxAttempt(ctx context.Context, etx Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], lggr logger.Logger) (attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) } diff --git a/common/txmgr/types/tx_store.go b/common/txmgr/types/tx_store.go index bca2d1e3647..2b82e1f6483 100644 --- a/common/txmgr/types/tx_store.go +++ b/common/txmgr/types/tx_store.go @@ -39,7 +39,7 @@ type TxStore[ FindTxesPendingCallback(ctx context.Context, blockNum int64, chainID CHAIN_ID) (receiptsPlus []ReceiptPlus[R], err error) // Update tx to mark that its callback has been signaled UpdateTxCallbackCompleted(ctx context.Context, pipelineTaskRunRid uuid.UUID, chainId CHAIN_ID) error - SaveFetchedReceipts(ctx context.Context, receipts []R, chainID CHAIN_ID) (err error) + SaveFetchedReceipts(ctx context.Context, r []R, state TxState, errorMsg *string, chainID CHAIN_ID) error // additional methods for tx store management CheckTxQueueCapacity(ctx context.Context, fromAddress ADDR, maxQueuedTransactions uint64, chainID CHAIN_ID) (err error) diff --git a/core/chains/evm/config/chain_scoped_transactions.go b/core/chains/evm/config/chain_scoped_transactions.go index df2343d3158..87031a4c66e 100644 --- a/core/chains/evm/config/chain_scoped_transactions.go +++ b/core/chains/evm/config/chain_scoped_transactions.go @@ -1,6 +1,7 @@ package config import ( + "net/url" "time" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -33,3 +34,27 @@ func (t *transactionsConfig) MaxInFlight() uint32 { func (t *transactionsConfig) MaxQueued() uint64 { return uint64(*t.c.MaxQueued) } + +func (t *transactionsConfig) AutoPurge() AutoPurgeConfig { + return &autoPurgeConfig{c: t.c.AutoPurge} +} + +type autoPurgeConfig struct { + c toml.AutoPurgeConfig +} + +func (a *autoPurgeConfig) Enabled() bool { + return *a.c.Enabled +} + +func (a *autoPurgeConfig) Threshold() uint32 { + return *a.c.Threshold +} + +func (a *autoPurgeConfig) MinAttempts() uint32 { + return *a.c.MinAttempts +} + +func (a *autoPurgeConfig) DetectionApiUrl() *url.URL { + return a.c.DetectionApiUrl.URL() +} diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index 34de754de21..18f63b8d918 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -2,6 +2,7 @@ package config import ( "math/big" + "net/url" "time" gethcommon "github.com/ethereum/go-ethereum/common" @@ -100,6 +101,14 @@ type Transactions interface { ReaperThreshold() time.Duration MaxInFlight() uint32 MaxQueued() uint64 + AutoPurge() AutoPurgeConfig +} + +type AutoPurgeConfig interface { + Enabled() bool + Threshold() uint32 + MinAttempts() uint32 + DetectionApiUrl() *url.URL } //go:generate mockery --quiet --name GasEstimator --output ./mocks/ --case=underscore diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index a326881bdde..a22f6e7f557 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -393,6 +393,44 @@ func (c *Chain) ValidateConfig() (err error) { Msg: "must be greater than or equal to 1"}) } + // AutoPurge configs depend on ChainType so handling validation on per chain basis + if c.Transactions.AutoPurge.Enabled != nil && *c.Transactions.AutoPurge.Enabled { + chainType := c.ChainType.ChainType() + switch chainType { + case config.ChainScroll: + if c.Transactions.AutoPurge.DetectionApiUrl == nil { + err = multierr.Append(err, commonconfig.ErrMissing{Name: "Transactions.AutoPurge.DetectionApiUrl", Msg: fmt.Sprintf("must be set for %s", chainType)}) + } else if c.Transactions.AutoPurge.DetectionApiUrl.IsZero() { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Transactions.AutoPurge.DetectionApiUrl", Value: c.Transactions.AutoPurge.DetectionApiUrl, Msg: fmt.Sprintf("must be set for %s", chainType)}) + } else { + switch c.Transactions.AutoPurge.DetectionApiUrl.Scheme { + case "http", "https": + default: + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Transactions.AutoPurge.DetectionApiUrl", Value: c.Transactions.AutoPurge.DetectionApiUrl.Scheme, Msg: "must be http or https"}) + } + } + case config.ChainZkEvm: + // No other configs are needed + default: + // Bump Threshold is required because the stuck tx heuristic relies on a minimum number of bump attempts to exist + if c.GasEstimator.BumpThreshold == nil { + err = multierr.Append(err, commonconfig.ErrMissing{Name: "GasEstimator.BumpThreshold", Msg: fmt.Sprintf("must be set if auto-purge feature is enabled for %s", chainType)}) + } else if *c.GasEstimator.BumpThreshold == 0 { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "GasEstimator.BumpThreshold", Value: 0, Msg: fmt.Sprintf("cannot be 0 if auto-purge feature is enabled for %s", chainType)}) + } + if c.Transactions.AutoPurge.Threshold == nil { + err = multierr.Append(err, commonconfig.ErrMissing{Name: "Transactions.AutoPurge.Threshold", Msg: fmt.Sprintf("needs to be set if auto-purge feature is enabled for %s", chainType)}) + } else if *c.Transactions.AutoPurge.Threshold == 0 { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Transactions.AutoPurge.Threshold", Value: 0, Msg: fmt.Sprintf("cannot be 0 if auto-purge feature is enabled for %s", chainType)}) + } + if c.Transactions.AutoPurge.MinAttempts == nil { + err = multierr.Append(err, commonconfig.ErrMissing{Name: "Transactions.AutoPurge.MinAttempts", Msg: fmt.Sprintf("needs to be set if auto-purge feature is enabled for %s", chainType)}) + } else if *c.Transactions.AutoPurge.MinAttempts == 0 { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Transactions.AutoPurge.MinAttempts", Value: 0, Msg: fmt.Sprintf("cannot be 0 if auto-purge feature is enabled for %s", chainType)}) + } + } + } + return } @@ -403,6 +441,8 @@ type Transactions struct { ReaperInterval *commonconfig.Duration ReaperThreshold *commonconfig.Duration ResendAfterThreshold *commonconfig.Duration + + AutoPurge AutoPurgeConfig `toml:",omitempty"` } func (t *Transactions) setFrom(f *Transactions) { @@ -424,6 +464,29 @@ func (t *Transactions) setFrom(f *Transactions) { if v := f.ResendAfterThreshold; v != nil { t.ResendAfterThreshold = v } + t.AutoPurge.setFrom(&f.AutoPurge) +} + +type AutoPurgeConfig struct { + Enabled *bool + Threshold *uint32 + MinAttempts *uint32 + DetectionApiUrl *commonconfig.URL +} + +func (a *AutoPurgeConfig) setFrom(f *AutoPurgeConfig) { + if v := f.Enabled; v != nil { + a.Enabled = v + } + if v := f.Threshold; v != nil { + a.Threshold = v + } + if v := f.MinAttempts; v != nil { + a.MinAttempts = v + } + if v := f.DetectionApiUrl; v != nil { + a.DetectionApiUrl = v + } } type OCR2 struct { diff --git a/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Cardona.toml b/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Cardona.toml index 9fa2b78e086..cd91465dae6 100644 --- a/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Cardona.toml +++ b/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Cardona.toml @@ -1,4 +1,5 @@ ChainID = '2442' +ChainType = 'zkevm' FinalityDepth = 500 NoNewHeadsThreshold = '12m' MinIncomingConfirmations = 1 diff --git a/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Goerli.toml b/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Goerli.toml index a259e4766f8..6a9b47190fd 100644 --- a/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Goerli.toml +++ b/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Goerli.toml @@ -1,4 +1,5 @@ ChainID = '1442' +ChainType = 'zkevm' FinalityDepth = 500 NoNewHeadsThreshold = '12m' MinIncomingConfirmations = 1 diff --git a/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Mainnet.toml b/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Mainnet.toml index e8833bc7312..79e0cb0fce5 100644 --- a/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Polygon_Zkevm_Mainnet.toml @@ -1,4 +1,5 @@ ChainID = '1101' +ChainType = 'zkevm' FinalityDepth = 500 NoNewHeadsThreshold = '6m' MinIncomingConfirmations = 1 diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index d65d0a1b0c1..9d4ddf7bf0a 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -23,6 +23,9 @@ ReaperInterval = '1h' ReaperThreshold = '168h' ResendAfterThreshold = '1m' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true diff --git a/core/chains/evm/txmgr/attempts.go b/core/chains/evm/txmgr/attempts.go index bf2d9a68edf..8566adcb5c5 100644 --- a/core/chains/evm/txmgr/attempts.go +++ b/core/chains/evm/txmgr/attempts.go @@ -37,6 +37,7 @@ type evmTxAttemptBuilderFeeConfig interface { TipCapMin() *assets.Wei PriceMin() *assets.Wei PriceMaxKey(common.Address) *assets.Wei + LimitDefault() uint64 } func NewEvmTxAttemptBuilder(chainID big.Int, feeConfig evmTxAttemptBuilderFeeConfig, keystore TxAttemptSigner[common.Address], estimator gas.EvmFeeEstimator) *evmTxAttemptBuilder { @@ -75,11 +76,41 @@ func (c *evmTxAttemptBuilder) NewBumpTxAttempt(ctx context.Context, etx Tx, prev if err != nil { return attempt, bumpedFee, bumpedFeeLimit, true, pkgerrors.Wrap(err, "failed to bump fee") // estimator errors are retryable } - + // If transaction's previous attempt is marked for purge, ensure the new bumped attempt also sends empty payload, 0 value, and LimitDefault as fee limit + if previousAttempt.IsPurgeAttempt { + etx.EncodedPayload = []byte{} + etx.Value = *big.NewInt(0) + bumpedFeeLimit = c.feeConfig.LimitDefault() + } attempt, retryable, err = c.NewCustomTxAttempt(ctx, etx, bumpedFee, bumpedFeeLimit, previousAttempt.TxType, lggr) + // If transaction's previous attempt is marked for purge, ensure the new bumped attempt is also marked for purge + if previousAttempt.IsPurgeAttempt { + attempt.IsPurgeAttempt = true + } return attempt, bumpedFee, bumpedFeeLimit, retryable, err } +func (c *evmTxAttemptBuilder) NewPurgeTxAttempt(ctx context.Context, etx Tx, lggr logger.Logger) (attempt TxAttempt, err error) { + // Use the LimitDefault since this is an empty tx + gasLimit := c.feeConfig.LimitDefault() + // Transactions being purged will always have a previous attempt since it had to have been broadcasted before at least once + previousAttempt := etx.TxAttempts[0] + keySpecificMaxGasPriceWei := c.feeConfig.PriceMaxKey(etx.FromAddress) + bumpedFee, _, err := c.EvmFeeEstimator.BumpFee(ctx, previousAttempt.TxFee, etx.FeeLimit, keySpecificMaxGasPriceWei, newEvmPriorAttempts(etx.TxAttempts)) + if err != nil { + return attempt, fmt.Errorf("failed to bump previous fee to use for the purge attempt: %w", err) + } + // Set empty payload and 0 value for purge attempts + etx.EncodedPayload = []byte{} + etx.Value = *big.NewInt(0) + attempt, _, err = c.NewCustomTxAttempt(ctx, etx, bumpedFee, gasLimit, previousAttempt.TxType, lggr) + if err != nil { + return attempt, fmt.Errorf("failed to create purge attempt: %w", err) + } + attempt.IsPurgeAttempt = true + return attempt, nil +} + // NewCustomTxAttempt is the lowest level func where the fee parameters + tx type must be passed in // used in the txm for force rebroadcast where fees and tx type are pre-determined without an estimator func (c *evmTxAttemptBuilder) NewCustomTxAttempt(ctx context.Context, etx Tx, fee gas.EvmFee, gasLimit uint64, txType int, lggr logger.Logger) (attempt TxAttempt, retryable bool, err error) { diff --git a/core/chains/evm/txmgr/attempts_test.go b/core/chains/evm/txmgr/attempts_test.go index d5c8f577ce1..52340ce51a5 100644 --- a/core/chains/evm/txmgr/attempts_test.go +++ b/core/chains/evm/txmgr/attempts_test.go @@ -36,6 +36,7 @@ type feeConfig struct { tipCapMin *assets.Wei priceMin *assets.Wei priceMax *assets.Wei + limitDefault uint64 } func newFeeConfig() *feeConfig { @@ -50,6 +51,7 @@ func (g *feeConfig) EIP1559DynamicFees() bool { return g. func (g *feeConfig) TipCapMin() *assets.Wei { return g.tipCapMin } func (g *feeConfig) PriceMin() *assets.Wei { return g.priceMin } func (g *feeConfig) PriceMaxKey(addr gethcommon.Address) *assets.Wei { return g.priceMax } +func (g *feeConfig) LimitDefault() uint64 { return g.limitDefault } func TestTxm_SignTx(t *testing.T) { t.Parallel() @@ -224,6 +226,87 @@ func TestTxm_NewLegacyAttempt(t *testing.T) { }) } +func TestTxm_NewPurgeAttempt(t *testing.T) { + addr := NewEvmAddress() + kst := ksmocks.NewEth(t) + tx := types.NewTx(&types.LegacyTx{}) + kst.On("SignTx", mock.Anything, addr, mock.Anything, big.NewInt(1)).Return(tx, nil) + gc := newFeeConfig() + gc.priceMin = assets.GWei(10) + gc.priceMax = assets.GWei(50) + gc.limitDefault = uint64(10) + est := gasmocks.NewEvmFeeEstimator(t) + bumpedLegacy := assets.GWei(30) + bumpedDynamicFee := assets.GWei(15) + bumpedDynamicTip := assets.GWei(10) + bumpedFee := gas.EvmFee{Legacy: bumpedLegacy, DynamicTipCap: bumpedDynamicTip, DynamicFeeCap: bumpedDynamicFee} + est.On("BumpFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(bumpedFee, uint64(10_000), nil) + cks := txmgr.NewEvmTxAttemptBuilder(*big.NewInt(1), gc, kst, est) + lggr := logger.Test(t) + ctx := testutils.Context(t) + + t.Run("creates legacy purge attempt with fields if previous attempt is legacy", func(t *testing.T) { + n := evmtypes.Nonce(0) + etx := txmgr.Tx{Sequence: &n, FromAddress: addr, EncodedPayload: []byte{1, 2, 3}} + prevAttempt, _, err := cks.NewCustomTxAttempt(ctx, etx, gas.EvmFee{Legacy: bumpedLegacy.Sub(assets.GWei(1))}, 100, 0x0, lggr) + require.NoError(t, err) + etx.TxAttempts = append(etx.TxAttempts, prevAttempt) + a, err := cks.NewPurgeTxAttempt(ctx, etx, lggr) + require.NoError(t, err) + // The fee limit is overridden with LimitDefault since purge attempts are just empty attempts + require.Equal(t, gc.limitDefault, a.ChainSpecificFeeLimit) + require.NotNil(t, a.TxFee.Legacy) + require.Equal(t, bumpedLegacy.String(), a.TxFee.Legacy.String()) + require.Nil(t, a.TxFee.DynamicTipCap) + require.Nil(t, a.TxFee.DynamicFeeCap) + require.Equal(t, true, a.IsPurgeAttempt) + require.Equal(t, []byte{}, a.Tx.EncodedPayload) + require.Equal(t, *big.NewInt(0), a.Tx.Value) + }) + + t.Run("creates dynamic purge attempt with fields if previous attempt is dynamic", func(t *testing.T) { + n := evmtypes.Nonce(0) + etx := txmgr.Tx{Sequence: &n, FromAddress: addr, EncodedPayload: []byte{1, 2, 3}} + prevAttempt, _, err := cks.NewCustomTxAttempt(ctx, etx, gas.EvmFee{DynamicTipCap: bumpedDynamicTip.Sub(assets.GWei(1)), DynamicFeeCap: bumpedDynamicFee.Sub(assets.GWei(1))}, 100, 0x2, lggr) + require.NoError(t, err) + etx.TxAttempts = append(etx.TxAttempts, prevAttempt) + a, err := cks.NewPurgeTxAttempt(ctx, etx, lggr) + require.NoError(t, err) + // The fee limit is overridden with LimitDefault since purge attempts are just empty attempts + require.Equal(t, gc.limitDefault, a.ChainSpecificFeeLimit) + require.Nil(t, a.TxFee.Legacy) + require.NotNil(t, a.TxFee.DynamicTipCap) + require.NotNil(t, a.TxFee.DynamicFeeCap) + require.Equal(t, bumpedDynamicTip.String(), a.TxFee.DynamicTipCap.String()) + require.Equal(t, bumpedDynamicFee.String(), a.TxFee.DynamicFeeCap.String()) + require.Equal(t, true, a.IsPurgeAttempt) + require.Equal(t, []byte{}, a.Tx.EncodedPayload) + require.Equal(t, *big.NewInt(0), a.Tx.Value) + }) + + t.Run("creates bump purge attempt with fields", func(t *testing.T) { + n := evmtypes.Nonce(0) + etx := txmgr.Tx{Sequence: &n, FromAddress: addr, EncodedPayload: []byte{1, 2, 3}} + prevAttempt, _, err := cks.NewCustomTxAttempt(ctx, etx, gas.EvmFee{Legacy: bumpedLegacy.Sub(assets.GWei(1))}, 100, 0x0, lggr) + require.NoError(t, err) + etx.TxAttempts = append(etx.TxAttempts, prevAttempt) + purgeAttempt, err := cks.NewPurgeTxAttempt(ctx, etx, lggr) + require.NoError(t, err) + etx.TxAttempts = append(etx.TxAttempts, purgeAttempt) + bumpAttempt, _, _, _, err := cks.NewBumpTxAttempt(ctx, etx, purgeAttempt, etx.TxAttempts, lggr) + require.NoError(t, err) + // The fee limit is overridden with LimitDefault since purge attempts are just empty attempts + require.Equal(t, gc.limitDefault, bumpAttempt.ChainSpecificFeeLimit) + require.NotNil(t, bumpAttempt.TxFee.Legacy) + require.Equal(t, bumpedLegacy.String(), bumpAttempt.TxFee.Legacy.String()) + require.Nil(t, bumpAttempt.TxFee.DynamicTipCap) + require.Nil(t, bumpAttempt.TxFee.DynamicFeeCap) + require.Equal(t, true, bumpAttempt.IsPurgeAttempt) + require.Equal(t, []byte{}, bumpAttempt.Tx.EncodedPayload) + require.Equal(t, *big.NewInt(0), bumpAttempt.Tx.Value) + }) +} + func TestTxm_NewCustomTxAttempt_NonRetryableErrors(t *testing.T) { t.Parallel() diff --git a/core/chains/evm/txmgr/builder.go b/core/chains/evm/txmgr/builder.go index 0671f49bb74..caba4d3806c 100644 --- a/core/chains/evm/txmgr/builder.go +++ b/core/chains/evm/txmgr/builder.go @@ -51,7 +51,8 @@ func NewTxm( chainID := txmClient.ConfiguredChainID() evmBroadcaster := NewEvmBroadcaster(txStore, txmClient, txmCfg, feeCfg, txConfig, listenerConfig, keyStore, txAttemptBuilder, lggr, checker, chainConfig.NonceAutoSync()) evmTracker := NewEvmTracker(txStore, keyStore, chainID, lggr) - evmConfirmer := NewEvmConfirmer(txStore, txmClient, txmCfg, feeCfg, txConfig, dbConfig, keyStore, txAttemptBuilder, lggr) + stuckTxDetector := NewStuckTxDetector(lggr, client.ConfiguredChainID(), chainConfig.ChainType(), fCfg.PriceMax(), txConfig.AutoPurge(), estimator, txStore, client) + evmConfirmer := NewEvmConfirmer(txStore, txmClient, txmCfg, feeCfg, txConfig, dbConfig, keyStore, txAttemptBuilder, lggr, stuckTxDetector) var evmResender *Resender if txConfig.ResendAfterThreshold() > 0 { evmResender = NewEvmResender(lggr, txStore, txmClient, evmTracker, keyStore, txmgr.DefaultResenderPollInterval, chainConfig, txConfig) @@ -109,8 +110,9 @@ func NewEvmConfirmer( keystore KeyStore, txAttemptBuilder TxAttemptBuilder, lggr logger.Logger, + stuckTxDetector StuckTxDetector, ) *Confirmer { - return txmgr.NewConfirmer(txStore, client, chainConfig, feeConfig, txConfig, dbConfig, keystore, txAttemptBuilder, lggr, func(r *evmtypes.Receipt) bool { return r == nil }) + return txmgr.NewConfirmer(txStore, client, chainConfig, feeConfig, txConfig, dbConfig, keystore, txAttemptBuilder, lggr, func(r *evmtypes.Receipt) bool { return r == nil }, stuckTxDetector) } // NewEvmTracker instantiates a new EVM tracker for abandoned transactions diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index db2d8a9092f..2ce34505234 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -129,7 +129,8 @@ func TestEthConfirmer_Lifecycle(t *testing.T) { ge := config.EVM().GasEstimator() feeEstimator := gas.NewEvmFeeEstimator(lggr, newEst, ge.EIP1559DynamicFees(), ge) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ethKeyStore, feeEstimator) - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), gconfig.Database(), ethKeyStore, txBuilder, lggr) + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), config.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), gconfig.Database(), ethKeyStore, txBuilder, lggr, stuckTxDetector) ctx := testutils.Context(t) // Can't close unstarted instance @@ -1645,8 +1646,9 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, kst, feeEstimator) addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", mock.Anything, &cltest.FixtureChainID).Return(addresses, nil).Maybe() + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), ccfg.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) // Create confirmer with necessary state - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr, stuckTxDetector) servicetest.Run(t, ec) currentHead := int64(30) oldEnough := int64(15) @@ -1693,7 +1695,8 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, kst, feeEstimator) addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", mock.Anything, &cltest.FixtureChainID).Return(addresses, nil).Maybe() - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr) + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), ccfg.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr, stuckTxDetector) servicetest.Run(t, ec) currentHead := int64(30) oldEnough := int64(15) @@ -3119,6 +3122,107 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { }) } +func TestEthConfirmer_ProcessStuckTransactions(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + ethKeyStore := cltest.NewKeyStore(t, db).Eth() + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(commonclient.Successful, nil).Once() + lggr := logger.Test(t) + feeEstimator := gasmocks.NewEvmFeeEstimator(t) + + // Return 10 gwei as market gas price + marketGasPrice := tenGwei + fee := gas.EvmFee{Legacy: marketGasPrice} + bumpedLegacy := assets.GWei(30) + bumpedFee := gas.EvmFee{Legacy: bumpedLegacy} + feeEstimator.On("GetFee", mock.Anything, []byte{}, uint64(0), mock.Anything).Return(fee, uint64(0), nil) + feeEstimator.On("BumpFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(bumpedFee, uint64(10_000), nil) + autoPurgeThreshold := uint32(5) + autoPurgeMinAttempts := uint32(3) + limitDefault := uint64(100) + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + c.EVM[0].GasEstimator.LimitDefault = ptr(limitDefault) + c.EVM[0].Transactions.AutoPurge.Enabled = ptr(true) + c.EVM[0].Transactions.AutoPurge.Threshold = ptr(autoPurgeThreshold) + c.EVM[0].Transactions.AutoPurge.MinAttempts = ptr(autoPurgeMinAttempts) + }) + evmcfg := evmtest.NewChainScopedConfig(t, cfg) + ge := evmcfg.EVM().GasEstimator() + txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ethKeyStore, feeEstimator) + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), evmcfg.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(evmcfg.EVM()), txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database(), ethKeyStore, txBuilder, lggr, stuckTxDetector) + servicetest.Run(t, ec) + + ctx := testutils.Context(t) + blockNum := int64(100) + + t.Run("detects and processes stuck transactions", func(t *testing.T) { + nonce := int64(0) + // Create attempts so that the oldest broadcast attempt's block num is what meets the threshold check + // Create autoPurgeMinAttempts number of attempts to ensure the broadcast attempt count check is not being triggered + // Create attempts broadcasted autoPurgeThreshold block ago to ensure broadcast block num check is not being triggered + tx := mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, nonce, fromAddress, autoPurgeMinAttempts, blockNum-int64(autoPurgeThreshold), marketGasPrice.Add(oneGwei)) + + head := evmtypes.Head{ + Hash: utils.NewHash(), + Number: blockNum, + } + ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(0), nil).Once() + ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Once() + + // First call to ProcessHead should: + // 1. Detect a stuck transaction + // 2. Create a purge attempt for it + // 3. Save the purge attempt to the DB + // 4. Send the purge attempt + err := ec.ProcessHead(ctx, &head) + require.NoError(t, err) + + // Check if the purge attempt was saved to the DB properly + dbTx, err := txStore.FindTxWithAttempts(ctx, tx.ID) + require.NoError(t, err) + require.NotNil(t, dbTx) + latestAttempt := dbTx.TxAttempts[0] + require.Equal(t, true, latestAttempt.IsPurgeAttempt) + require.Equal(t, limitDefault, latestAttempt.ChainSpecificFeeLimit) + require.Equal(t, bumpedFee.Legacy, latestAttempt.TxFee.Legacy) + + head = evmtypes.Head{ + Hash: utils.NewHash(), + Number: blockNum + 1, + } + ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(1), nil) + ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { + return len(b) == 4 && cltest.BatchElemMatchesParams(b[0], latestAttempt.Hash, "eth_getTransactionReceipt") + })).Return(nil).Run(func(args mock.Arguments) { + elems := args.Get(1).([]rpc.BatchElem) + // First transaction confirmed + *(elems[0].Result.(*evmtypes.Receipt)) = evmtypes.Receipt{ + TxHash: latestAttempt.Hash, + BlockHash: utils.NewHash(), + BlockNumber: big.NewInt(blockNum + 1), + TransactionIndex: uint(1), + Status: uint64(1), + } + }).Once() + + // Second call to ProcessHead on next head should: + // 1. Check for receipts for purged transaction + // 2. When receipts are found for a purge attempt, the transaction is marked in the DB as fatal error with error message + err = ec.ProcessHead(ctx, &head) + require.NoError(t, err) + dbTx, err = txStore.FindTxWithAttempts(ctx, tx.ID) + require.NoError(t, err) + require.NotNil(t, dbTx) + require.Equal(t, txmgrcommon.TxFatalError, dbTx.State) + require.Equal(t, "transaction terminally stuck", dbTx.Error.String) + }) +} + func ptr[T any](t T) *T { return &t } func newEthConfirmer(t testing.TB, txStore txmgr.EvmTxStore, ethClient client.Client, gconfig chainlink.GeneralConfig, config evmconfig.ChainScopedConfig, ks keystore.Eth, fn txmgrcommon.ResumeCallback) *txmgr.Confirmer { @@ -3128,7 +3232,8 @@ func newEthConfirmer(t testing.TB, txStore txmgr.EvmTxStore, ethClient client.Cl return gas.NewFixedPriceEstimator(ge, nil, ge.BlockHistory(), lggr, nil) }, ge.EIP1559DynamicFees(), ge) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ks, estimator) - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), gconfig.Database(), ks, txBuilder, lggr) + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), config.EVM().Transactions().AutoPurge(), estimator, txStore, ethClient) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), gconfig.Database(), ks, txBuilder, lggr, stuckTxDetector) ec.SetResumeCallback(fn) servicetest.Run(t, ec) return ec diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 505938d3026..ce64f816cd9 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -48,7 +48,7 @@ type EvmTxStore interface { TxStoreWebApi } -// TxStoreWebApi encapsulates the methods that are not used by the txmgr and only used by the various web controllers and readers +// TxStoreWebApi encapsulates the methods that are not used by the txmgr and only used by the various web controllers, readers, or evm specific components type TxStoreWebApi interface { FindTxAttemptConfirmedByTxIDs(ctx context.Context, ids []int64) ([]TxAttempt, error) FindTxByHash(ctx context.Context, hash common.Hash) (*Tx, error) @@ -57,6 +57,7 @@ type TxStoreWebApi interface { TransactionsWithAttempts(ctx context.Context, offset, limit int) ([]Tx, int, error) FindTxAttempt(ctx context.Context, hash common.Hash) (*TxAttempt, error) FindTxWithAttempts(ctx context.Context, etxID int64) (etx Tx, err error) + FindTxsByStateAndFromAddresses(ctx context.Context, addresses []common.Address, state txmgrtypes.TxState, chainID *big.Int) (txs []*Tx, err error) } type TestEvmTxStore interface { @@ -285,6 +286,7 @@ type DbEthTxAttempt struct { TxType int GasTipCap *assets.Wei GasFeeCap *assets.Wei + IsPurgeAttempt bool } func (db *DbEthTxAttempt) FromTxAttempt(attempt *TxAttempt) { @@ -299,6 +301,7 @@ func (db *DbEthTxAttempt) FromTxAttempt(attempt *TxAttempt) { db.TxType = attempt.TxType db.GasTipCap = attempt.TxFee.DynamicTipCap db.GasFeeCap = attempt.TxFee.DynamicFeeCap + db.IsPurgeAttempt = attempt.IsPurgeAttempt // handle state naming difference between generic + EVM if attempt.State == txmgrtypes.TxAttemptInsufficientFunds { @@ -330,6 +333,7 @@ func (db DbEthTxAttempt) ToTxAttempt(attempt *TxAttempt) { DynamicTipCap: db.GasTipCap, DynamicFeeCap: db.GasFeeCap, } + attempt.IsPurgeAttempt = db.IsPurgeAttempt } func dbEthTxAttemptsToEthTxAttempts(dbEthTxAttempt []DbEthTxAttempt) []TxAttempt { @@ -353,8 +357,8 @@ func NewTxStore( } const insertIntoEthTxAttemptsQuery = ` -INSERT INTO evm.tx_attempts (eth_tx_id, gas_price, signed_raw_tx, hash, broadcast_before_block_num, state, created_at, chain_specific_gas_limit, tx_type, gas_tip_cap, gas_fee_cap) -VALUES (:eth_tx_id, :gas_price, :signed_raw_tx, :hash, :broadcast_before_block_num, :state, NOW(), :chain_specific_gas_limit, :tx_type, :gas_tip_cap, :gas_fee_cap) +INSERT INTO evm.tx_attempts (eth_tx_id, gas_price, signed_raw_tx, hash, broadcast_before_block_num, state, created_at, chain_specific_gas_limit, tx_type, gas_tip_cap, gas_fee_cap, is_purge_attempt) +VALUES (:eth_tx_id, :gas_price, :signed_raw_tx, :hash, :broadcast_before_block_num, :state, NOW(), :chain_specific_gas_limit, :tx_type, :gas_tip_cap, :gas_fee_cap, :is_purge_attempt) RETURNING *; ` @@ -822,7 +826,39 @@ ORDER BY evm.txes.nonce ASC, evm.tx_attempts.gas_price DESC, evm.tx_attempts.gas return } -func (o *evmTxStore) SaveFetchedReceipts(ctx context.Context, r []*evmtypes.Receipt, chainID *big.Int) (err error) { +// Returns the transaction by state and from addresses +// Loads attempt and receipts in the transactions +func (o *evmTxStore) FindTxsByStateAndFromAddresses(ctx context.Context, addresses []common.Address, state txmgrtypes.TxState, chainID *big.Int) (txs []*Tx, err error) { + var cancel context.CancelFunc + ctx, cancel = o.stopCh.Ctx(ctx) + defer cancel() + enabledAddrsBytea := make([][]byte, len(addresses)) + for i, addr := range addresses { + enabledAddrsBytea[i] = addr.Bytes() + } + err = o.Transact(ctx, true, func(orm *evmTxStore) error { + var dbEtxs []DbEthTx + err = orm.q.SelectContext(ctx, &dbEtxs, `SELECT * FROM evm.txes WHERE state = $1 AND from_address = ANY($2) AND evm_chain_id = $3`, state, enabledAddrsBytea, chainID.String()) + if err != nil { + return fmt.Errorf("FindTxsByStateAndFromAddresses failed to load evm.txes: %w", err) + } + if len(dbEtxs) == 0 { + return nil + } + txs = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txs) + if err = orm.LoadTxesAttempts(ctx, txs); err != nil { + return fmt.Errorf("FindTxsByStateAndFromAddresses failed to load evm.tx_attempts: %w", err) + } + if err = orm.loadEthTxesAttemptsReceipts(ctx, txs); err != nil { + return fmt.Errorf("FindTxsByStateAndFromAddresses failed to load evm.receipts: %w", err) + } + return nil + }) + return +} + +func (o *evmTxStore) SaveFetchedReceipts(ctx context.Context, r []*evmtypes.Receipt, state txmgrtypes.TxState, errorMsg *string, chainID *big.Int) (err error) { var cancel context.CancelFunc ctx, cancel = o.stopCh.Ctx(ctx) defer cancel() @@ -869,7 +905,7 @@ func (o *evmTxStore) SaveFetchedReceipts(ctx context.Context, r []*evmtypes.Rece valueStrs = append(valueStrs, "(?,?,?,?,?,NOW())") valueArgs = append(valueArgs, r.TxHash, r.BlockHash, r.BlockNumber.Int64(), r.TransactionIndex, receiptJSON) } - valueArgs = append(valueArgs, chainID.String()) + valueArgs = append(valueArgs, state, errorMsg, chainID.String()) /* #nosec G201 */ sql := ` @@ -892,7 +928,7 @@ func (o *evmTxStore) SaveFetchedReceipts(ctx context.Context, r []*evmtypes.Rece RETURNING evm.tx_attempts.eth_tx_id ) UPDATE evm.txes - SET state = 'confirmed' + SET state = ?, error = ? FROM updated_eth_tx_attempts WHERE updated_eth_tx_attempts.eth_tx_id = evm.txes.id AND evm_chain_id = ? diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index 20b39f3a83e..23b8a9fde33 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -521,7 +521,7 @@ func TestORM_SaveFetchedReceipts(t *testing.T) { TransactionIndex: uint(1), } - err := txStore.SaveFetchedReceipts(testutils.Context(t), []*evmtypes.Receipt{&txmReceipt}, ethClient.ConfiguredChainID()) + err := txStore.SaveFetchedReceipts(testutils.Context(t), []*evmtypes.Receipt{&txmReceipt}, txmgrcommon.TxConfirmed, nil, ethClient.ConfiguredChainID()) require.NoError(t, err) etx0, err = txStore.FindTxWithAttempts(ctx, etx0.ID) @@ -1194,8 +1194,8 @@ func TestORM_LoadEthTxesAttempts(t *testing.T) { tx, err := db.BeginTx(ctx, nil) require.NoError(t, err) - const insertEthTxAttemptSQL = `INSERT INTO evm.tx_attempts (eth_tx_id, gas_price, signed_raw_tx, hash, broadcast_before_block_num, state, created_at, chain_specific_gas_limit, tx_type, gas_tip_cap, gas_fee_cap) VALUES ( - :eth_tx_id, :gas_price, :signed_raw_tx, :hash, :broadcast_before_block_num, :state, NOW(), :chain_specific_gas_limit, :tx_type, :gas_tip_cap, :gas_fee_cap + const insertEthTxAttemptSQL = `INSERT INTO evm.tx_attempts (eth_tx_id, gas_price, signed_raw_tx, hash, broadcast_before_block_num, state, created_at, chain_specific_gas_limit, tx_type, gas_tip_cap, gas_fee_cap, is_purge_attempt) VALUES ( + :eth_tx_id, :gas_price, :signed_raw_tx, :hash, :broadcast_before_block_num, :state, NOW(), :chain_specific_gas_limit, :tx_type, :gas_tip_cap, :gas_fee_cap, :is_purge_attempt ) RETURNING *` query, args, err := sqlutil.DataSource(db).BindNamed(insertEthTxAttemptSQL, dbAttempt) require.NoError(t, err) diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index be59d0130a4..465681247e2 100644 --- a/core/chains/evm/txmgr/mocks/evm_tx_store.go +++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go @@ -761,6 +761,36 @@ func (_m *EvmTxStore) FindTxesWithMetaFieldByStates(ctx context.Context, metaFie return r0, r1 } +// FindTxsByStateAndFromAddresses provides a mock function with given fields: ctx, addresses, state, chainID +func (_m *EvmTxStore) FindTxsByStateAndFromAddresses(ctx context.Context, addresses []common.Address, state types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { + ret := _m.Called(ctx, addresses, state, chainID) + + if len(ret) == 0 { + panic("no return value specified for FindTxsByStateAndFromAddresses") + } + + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []common.Address, types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { + return rf(ctx, addresses, state, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, []common.Address, types.TxState, *big.Int) []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok { + r0 = rf(ctx, addresses, state, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []common.Address, types.TxState, *big.Int) error); ok { + r1 = rf(ctx, addresses, state, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindTxsRequiringGasBump provides a mock function with given fields: ctx, address, blockNum, gasBumpThreshold, depth, chainID func (_m *EvmTxStore) FindTxsRequiringGasBump(ctx context.Context, address common.Address, blockNum int64, gasBumpThreshold int64, depth int64, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, address, blockNum, gasBumpThreshold, depth, chainID) @@ -1135,17 +1165,17 @@ func (_m *EvmTxStore) SaveConfirmedMissingReceiptAttempt(ctx context.Context, ti return r0 } -// SaveFetchedReceipts provides a mock function with given fields: ctx, receipts, chainID -func (_m *EvmTxStore) SaveFetchedReceipts(ctx context.Context, receipts []*evmtypes.Receipt, chainID *big.Int) error { - ret := _m.Called(ctx, receipts, chainID) +// SaveFetchedReceipts provides a mock function with given fields: ctx, r, state, errorMsg, chainID +func (_m *EvmTxStore) SaveFetchedReceipts(ctx context.Context, r []*evmtypes.Receipt, state types.TxState, errorMsg *string, chainID *big.Int) error { + ret := _m.Called(ctx, r, state, errorMsg, chainID) if len(ret) == 0 { panic("no return value specified for SaveFetchedReceipts") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []*evmtypes.Receipt, *big.Int) error); ok { - r0 = rf(ctx, receipts, chainID) + if rf, ok := ret.Get(0).(func(context.Context, []*evmtypes.Receipt, types.TxState, *string, *big.Int) error); ok { + r0 = rf(ctx, r, state, errorMsg, chainID) } else { r0 = ret.Error(0) } diff --git a/core/chains/evm/txmgr/models.go b/core/chains/evm/txmgr/models.go index be06f5dd5e9..f8682ffd500 100644 --- a/core/chains/evm/txmgr/models.go +++ b/core/chains/evm/txmgr/models.go @@ -38,6 +38,7 @@ type ( TxAttempt = txmgrtypes.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] Receipt = dbReceipt // EvmReceipt is the exported DB table model for receipts ReceiptPlus = txmgrtypes.ReceiptPlus[*evmtypes.Receipt] + StuckTxDetector = txmgrtypes.StuckTxDetector[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] TxmClient = txmgrtypes.TxmClient[*big.Int, common.Address, common.Hash, common.Hash, *evmtypes.Receipt, evmtypes.Nonce, gas.EvmFee] TransactionClient = txmgrtypes.TransactionClient[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] ChainReceipt = txmgrtypes.ChainReceipt[common.Hash, common.Hash] diff --git a/core/chains/evm/txmgr/stuck_tx_detector.go b/core/chains/evm/txmgr/stuck_tx_detector.go new file mode 100644 index 00000000000..d48cdf00e79 --- /dev/null +++ b/core/chains/evm/txmgr/stuck_tx_detector.go @@ -0,0 +1,375 @@ +package txmgr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "math/big" + "net/http" + "net/url" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/common/config" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" + "github.com/smartcontractkit/chainlink/v2/common/txmgr" + "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" +) + +type stuckTxDetectorGasEstimator interface { + GetFee(ctx context.Context, calldata []byte, feeLimit uint64, maxFeePrice *assets.Wei, opts ...feetypes.Opt) (fee gas.EvmFee, chainSpecificFeeLimit uint64, err error) +} + +type stuckTxDetectorClient interface { + BatchCallContext(ctx context.Context, b []rpc.BatchElem) error +} + +type stuckTxDetectorTxStore interface { + FindTxsByStateAndFromAddresses(ctx context.Context, addresses []common.Address, state types.TxState, chainID *big.Int) (txs []*Tx, err error) +} + +type stuckTxDetectorConfig interface { + Enabled() bool + Threshold() uint32 + MinAttempts() uint32 + DetectionApiUrl() *url.URL +} + +type stuckTxDetector struct { + lggr logger.Logger + chainID *big.Int + chainType config.ChainType + maxPrice *assets.Wei + cfg stuckTxDetectorConfig + + gasEstimator stuckTxDetectorGasEstimator + txStore stuckTxDetectorTxStore + chainClient stuckTxDetectorClient + httpClient *http.Client + + purgeBlockNumLock sync.RWMutex + purgeBlockNumMap map[common.Address]int64 // Tracks the last block num a tx was purged for each from address if the PurgeOverflowTxs feature is enabled +} + +func NewStuckTxDetector(lggr logger.Logger, chainID *big.Int, chainType config.ChainType, maxPrice *assets.Wei, cfg stuckTxDetectorConfig, gasEstimator stuckTxDetectorGasEstimator, txStore stuckTxDetectorTxStore, chainClient stuckTxDetectorClient) *stuckTxDetector { + t := http.DefaultTransport.(*http.Transport).Clone() + t.DisableCompression = true + httpClient := &http.Client{Transport: t} + return &stuckTxDetector{ + lggr: lggr, + chainID: chainID, + chainType: chainType, + maxPrice: maxPrice, + cfg: cfg, + gasEstimator: gasEstimator, + txStore: txStore, + chainClient: chainClient, + httpClient: httpClient, + purgeBlockNumMap: make(map[common.Address]int64), + } +} + +func (d *stuckTxDetector) LoadPurgeBlockNumMap(ctx context.Context, addresses []common.Address) error { + // Skip loading purge block num map if auto-purge feature disabled or Threshold is set to 0 + if !d.cfg.Enabled() || d.cfg.Threshold() == 0 { + return nil + } + d.purgeBlockNumLock.Lock() + defer d.purgeBlockNumLock.Unlock() + // Ok to reset the map here since this method could be reloaded with a new list of from addresses + d.purgeBlockNumMap = make(map[common.Address]int64) + for _, address := range addresses { + d.purgeBlockNumMap[address] = 0 + } + + // Find all fatal error transactions to see if any were from previous purges to properly set the map + txs, err := d.txStore.FindTxsByStateAndFromAddresses(ctx, addresses, txmgr.TxFatalError, d.chainID) + if err != nil { + return fmt.Errorf("failed to query fatal error transactions from the txstore: %w", err) + } + + // Set the purgeBlockNumMap with the receipt block num of purge attempts + for _, tx := range txs { + for _, attempt := range tx.TxAttempts { + if attempt.IsPurgeAttempt && len(attempt.Receipts) > 0 { + // There should only be 1 receipt in an attempt for a transaction + d.purgeBlockNumMap[tx.FromAddress] = attempt.Receipts[0].GetBlockNumber().Int64() + break + } + } + } + + return nil +} + +// If the auto-purge feature is enabled, finds terminally stuck transactions +// Uses a chain specific method for detection, or if one does not exist, applies a general heuristic +func (d *stuckTxDetector) DetectStuckTransactions(ctx context.Context, enabledAddresses []common.Address, blockNum int64) ([]Tx, error) { + if !d.cfg.Enabled() { + return nil, nil + } + txs, err := d.FindUnconfirmedTxWithLowestNonce(ctx, enabledAddresses) + if err != nil { + return nil, fmt.Errorf("failed to get list of transactions waiting confirmations with lowest nonce for distinct from addresses: %w", err) + } + // No transactions found + if len(txs) == 0 { + return nil, nil + } + + switch d.chainType { + case config.ChainScroll: + return d.detectStuckTransactionsScroll(ctx, txs) + case config.ChainZkEvm: + return d.detectStuckTransactionsZkEVM(ctx, txs) + default: + return d.detectStuckTransactionsHeuristic(ctx, txs, blockNum) + } +} + +// Finds the lowest nonce Unconfirmed transaction for each enabled address +// Only the earliest transaction can be considered terminally stuck. All others may be valid and just stuck behind the nonce +func (d *stuckTxDetector) FindUnconfirmedTxWithLowestNonce(ctx context.Context, enabledAddresses []common.Address) ([]Tx, error) { + // Loads attempts within tx + txs, err := d.txStore.FindTxsByStateAndFromAddresses(ctx, enabledAddresses, txmgr.TxUnconfirmed, d.chainID) + if err != nil { + return nil, fmt.Errorf("failed to retrieve unconfirmed transactions for enabled addresses: %w", err) + } + // Stores the lowest nonce tx found in the query results for each from address + lowestNonceTxMap := make(map[common.Address]Tx) + for _, tx := range txs { + if _, ok := lowestNonceTxMap[tx.FromAddress]; !ok { + lowestNonceTxMap[tx.FromAddress] = *tx + } else if lowestNonceTx := lowestNonceTxMap[tx.FromAddress]; *lowestNonceTx.Sequence > *tx.Sequence { + lowestNonceTxMap[tx.FromAddress] = *tx + } + } + + // Build list of potentially stuck tx but exclude any that are already marked for purge + var stuckTxs []Tx + for _, tx := range lowestNonceTxMap { + // Attempts are loaded newest to oldest so one marked for purge will always be first + if len(tx.TxAttempts) > 0 && !tx.TxAttempts[0].IsPurgeAttempt { + stuckTxs = append(stuckTxs, tx) + } + } + + return stuckTxs, nil +} + +// Uses a heuristic to determine a stuck transaction potentially due to overflow +// This method can be unreliable and may result in false positives but it is best effort to keep the TXM from getting blocked +// 1. Check if Threshold amount of blocks have passed since the last purge of a tx for the same fromAddress +// 2. If 1 is true, check if Threshold amount of blocks have passed since the initial broadcast +// 3. If 2 is true, check if the transaction has at least MinAttempts amount of broadcasted attempts +// 4. If 3 is true, check if the latest attempt's gas price is higher than what our gas estimator's GetFee method returns +// 5. If 4 is true, the transaction is likely stuck due to overflow +func (d *stuckTxDetector) detectStuckTransactionsHeuristic(ctx context.Context, txs []Tx, blockNum int64) ([]Tx, error) { + d.purgeBlockNumLock.RLock() + defer d.purgeBlockNumLock.RUnlock() + // Get gas price from internal gas estimator + // Send with max gas price time 2 to prevent the results from being capped. Need the market gas price here. + marketGasPrice, _, err := d.gasEstimator.GetFee(ctx, []byte{}, 0, d.maxPrice.Mul(big.NewInt(2))) + if err != nil { + return txs, fmt.Errorf("failed to get market gas price for overflow detection: %w", err) + } + var stuckTxs []Tx + for _, tx := range txs { + // 1. Check if Threshold amount of blocks have passed since the last purge of a tx for the same fromAddress + // Used to rate limit purging to prevent a potential valid tx that was stuck behind an overflow tx from also getting purged without having enough time to be confirmed + d.purgeBlockNumLock.RLock() + lastPurgeBlockNum := d.purgeBlockNumMap[tx.FromAddress] + d.purgeBlockNumLock.RUnlock() + if lastPurgeBlockNum > blockNum-int64(d.cfg.Threshold()) { + continue + } + // Tx attempts are loaded from newest to oldest + oldestBroadcastAttempt, newestBroadcastAttempt, broadcastedAttemptsCount := findBroadcastedAttempts(tx) + // 2. Check if Threshold amount of blocks have passed since the oldest attempt's broadcast block num + if *oldestBroadcastAttempt.BroadcastBeforeBlockNum > blockNum-int64(d.cfg.Threshold()) { + continue + } + // 3. Check if the transaction has at least MinAttempts amount of broadcasted attempts + if broadcastedAttemptsCount < d.cfg.MinAttempts() { + continue + } + // 4. Check if the newest broadcasted attempt's gas price is higher than what our gas estimator's GetFee method returns + if compareGasFees(newestBroadcastAttempt.TxFee, marketGasPrice) <= 0 { + continue + } + // 5. Return the transaction since it is likely stuck due to overflow + stuckTxs = append(stuckTxs, tx) + } + return stuckTxs, nil +} + +func compareGasFees(attemptGas gas.EvmFee, marketGas gas.EvmFee) int { + if attemptGas.Legacy != nil && marketGas.Legacy != nil { + return attemptGas.Legacy.Cmp(marketGas.Legacy) + } + if attemptGas.DynamicFeeCap.Cmp(marketGas.DynamicFeeCap) == 0 { + return attemptGas.DynamicTipCap.Cmp(marketGas.DynamicTipCap) + } + return attemptGas.DynamicFeeCap.Cmp(marketGas.DynamicFeeCap) +} + +// Assumes tx attempts are loaded newest to oldest +func findBroadcastedAttempts(tx Tx) (oldestAttempt TxAttempt, newestAttempt TxAttempt, broadcastedCount uint32) { + foundNewest := false + for _, attempt := range tx.TxAttempts { + if attempt.State != types.TxAttemptBroadcast { + continue + } + if !foundNewest { + newestAttempt = attempt + foundNewest = true + } + oldestAttempt = attempt + broadcastedCount++ + } + return +} + +type scrollRequest struct { + Txs []string `json:"txs"` +} + +type scrollResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + Data map[string]int `json:"data"` +} + +// Uses the custom Scroll skipped endpoint to determine an overflow transaction +func (d *stuckTxDetector) detectStuckTransactionsScroll(ctx context.Context, txs []Tx) ([]Tx, error) { + if d.cfg.DetectionApiUrl() == nil { + return nil, fmt.Errorf("expected DetectionApiUrl config to be set for chain type: %s", d.chainType) + } + + attemptHashMap := make(map[string]Tx) + + request := new(scrollRequest) + // Populate the request with the tx hash of the latest broadcast attempt from every tx + for _, tx := range txs { + for _, attempt := range tx.TxAttempts { + if attempt.State == types.TxAttemptBroadcast { + request.Txs = append(request.Txs, attempt.Hash.String()) + attemptHashMap[attempt.Hash.String()] = tx + break + } + } + } + jsonReq, err := json.Marshal(request) + if err != nil { + return nil, fmt.Errorf("failed to marshal json request %v for custom endpoint: %w", request, err) + } + + // Build http post request + url := fmt.Sprintf("%s/v1/sequencer/tx/skipped", d.cfg.DetectionApiUrl()) + bodyReader := bytes.NewReader(jsonReq) + postReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bodyReader) + if err != nil { + return nil, fmt.Errorf("failed to make new request with context: %w", err) + } + // Send request + resp, err := d.httpClient.Do(postReq) + if err != nil { + return nil, fmt.Errorf("request to scroll's custom endpoint failed: %w", err) + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + return nil, fmt.Errorf("request failed with status %d", resp.StatusCode) + } + // Decode the response into expected type + scrollResp := new(scrollResponse) + err = json.NewDecoder(resp.Body).Decode(scrollResp) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal response into struct: %w", err) + } + if scrollResp.Errcode != 0 || scrollResp.Errmsg != "" { + return nil, fmt.Errorf("scroll's custom endpoint returned an error with code: %d, message: %s", scrollResp.Errcode, scrollResp.Errmsg) + } + + // Return all transactions marked with status 1 signaling they have been skipped due to overflow + var stuckTx []Tx + for hash, status := range scrollResp.Data { + if status == 1 { + stuckTx = append(stuckTx, attemptHashMap[hash]) + } + } + + return stuckTx, nil +} + +// Uses eth_getTransactionByHash to detect that a transaction has been discarded due to overflow +// Currently only used by zkEVM but if other chains follow the same behavior in the future +func (d *stuckTxDetector) detectStuckTransactionsZkEVM(ctx context.Context, txs []Tx) ([]Tx, error) { + txReqs := make([]rpc.BatchElem, len(txs)) + txHashMap := make(map[common.Hash]Tx) + txRes := make([]*map[string]interface{}, len(txs)) + + // Build batch request elems to perform + // Does not need to be separated out into smaller batches + // Max number of transactions to check is equal to the number of enabled addresses which is a relatively small amount + for i, tx := range txs { + latestAttemptHash := tx.TxAttempts[0].Hash + var result map[string]interface{} + txReqs[i] = rpc.BatchElem{ + Method: "eth_getTransactionByHash", + Args: []interface{}{ + latestAttemptHash, + }, + Result: &result, + } + txHashMap[latestAttemptHash] = tx + txRes[i] = &result + } + + // Send batch request + err := d.chainClient.BatchCallContext(ctx, txReqs) + if err != nil { + return nil, fmt.Errorf("failed to get transactions by hash in batch: %w", err) + } + + // Parse results to find tx skipped due to zk overflow + // If the result is nil, the transaction was discarded due to overflow + var stuckTxs []Tx + for i, req := range txReqs { + txHash := req.Args[0].(common.Hash) + if req.Error != nil { + d.lggr.Debugf("failed to get transaction by hash (%s): %w", txHash.String(), req.Error) + continue + } + result := *txRes[i] + if result == nil { + tx := txHashMap[txHash] + stuckTxs = append(stuckTxs, tx) + } + } + return stuckTxs, nil +} + +// Once a purged tx's empty attempt is confirmed, this method is used to set at which block num the tx was purged at for the fromAddress +func (d *stuckTxDetector) SetPurgeBlockNum(fromAddress common.Address, blockNum int64) { + d.purgeBlockNumLock.Lock() + defer d.purgeBlockNumLock.Unlock() + d.purgeBlockNumMap[fromAddress] = blockNum +} + +func (d *stuckTxDetector) StuckTxFatalError() *string { + var errorMsg string + switch d.chainType { + case config.ChainScroll, config.ChainZkEvm: + errorMsg = "transaction skipped by chain" + default: + errorMsg = "transaction terminally stuck" + } + + return &errorMsg +} diff --git a/core/chains/evm/txmgr/stuck_tx_detector_test.go b/core/chains/evm/txmgr/stuck_tx_detector_test.go new file mode 100644 index 00000000000..39c275d286f --- /dev/null +++ b/core/chains/evm/txmgr/stuck_tx_detector_test.go @@ -0,0 +1,433 @@ +package txmgr_test + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" + gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" +) + +var ( + tenGwei = assets.NewWeiI(10_000_000_000) + oneGwei = assets.NewWeiI(1_000_000_000) +) + +func TestStuckTxDetector_Disabled(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + ethKeyStore := cltest.NewKeyStore(t, db).Eth() + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + + lggr := logger.Test(t) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + feeEstimator := gasmocks.NewEvmFeeEstimator(t) + autoPurgeCfg := testAutoPurgeConfig{ + enabled: false, + } + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), autoPurgeCfg, feeEstimator, txStore, ethClient) + + t.Run("returns empty list if auto-purge feature is disabled", func(t *testing.T) { + txs, err := stuckTxDetector.DetectStuckTransactions(testutils.Context(t), []common.Address{fromAddress}, 100) + require.NoError(t, err) + require.Len(t, txs, 0) + }) +} + +func TestStuckTxDetector_LoadPurgeBlockNumMap(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + ethKeyStore := cltest.NewKeyStore(t, db).Eth() + ctx := testutils.Context(t) + blockNum := int64(100) + + lggr := logger.Test(t) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + feeEstimator := gasmocks.NewEvmFeeEstimator(t) + marketGasPrice := assets.GWei(15) + fee := gas.EvmFee{Legacy: marketGasPrice} + feeEstimator.On("GetFee", mock.Anything, []byte{}, uint64(0), mock.Anything).Return(fee, uint64(0), nil) + autoPurgeThreshold := uint32(5) + autoPurgeMinAttempts := uint32(3) + autoPurgeCfg := testAutoPurgeConfig{ + enabled: true, // Enable auto-purge feature for testing + threshold: autoPurgeThreshold, + minAttempts: autoPurgeMinAttempts, + } + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), autoPurgeCfg, feeEstimator, txStore, ethClient) + + t.Run("purge num map loaded on startup rate limits new purges on startup", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + mustInsertFatalErrorTxWithError(t, txStore, 0, fromAddress, blockNum) + + err := stuckTxDetector.LoadPurgeBlockNumMap(ctx, []common.Address{fromAddress}) + require.NoError(t, err) + + enabledAddresses := []common.Address{fromAddress} + // Create attempts broadcasted autoPurgeThreshold block ago to ensure broadcast block num check is not being triggered + // Create autoPurgeMinAttempts number of attempts to ensure the broadcast attempt count check is not being triggered + // Create attempts so that the latest has a higher gas price than the market to ensure the gas price check is not being triggered + mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 1, fromAddress, autoPurgeMinAttempts, blockNum-int64(autoPurgeThreshold), marketGasPrice.Add(oneGwei)) + + // Run detection logic on autoPurgeThreshold blocks past the latest broadcast attempt + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, enabledAddresses, blockNum) + require.NoError(t, err) + require.Len(t, txs, 0) + }) +} + +func TestStuckTxDetector_FindPotentialStuckTxs(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + _, config := newTestChainScopedConfig(t) + txStore := cltest.NewTestTxStore(t, db) + ethKeyStore := cltest.NewKeyStore(t, db).Eth() + ctx := testutils.Context(t) + + lggr := logger.Test(t) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + feeEstimator := gasmocks.NewEvmFeeEstimator(t) + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), config.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) + + t.Run("returns empty list if no unconfimed transactions found", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + stuckTxs, err := stuckTxDetector.FindUnconfirmedTxWithLowestNonce(ctx, []common.Address{fromAddress}) + require.NoError(t, err) + require.Len(t, stuckTxs, 0) + }) + + t.Run("returns 1 unconfirmed transaction for each unique from address", func(t *testing.T) { + _, fromAddress1 := cltest.MustInsertRandomKey(t, ethKeyStore) + _, fromAddress2 := cltest.MustInsertRandomKey(t, ethKeyStore) + // Insert 2 txs for from address, should only return the lowest nonce txs + cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress1) + cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress1) + // Insert 1 tx for other from address, should return a tx + cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress2) + stuckTxs, err := stuckTxDetector.FindUnconfirmedTxWithLowestNonce(ctx, []common.Address{fromAddress1, fromAddress2}) + require.NoError(t, err) + + require.Len(t, stuckTxs, 2) + var foundFromAddresses []common.Address + for _, stuckTx := range stuckTxs { + // Make sure lowest nonce tx is returned for both from addresses + require.Equal(t, types.Nonce(0), *stuckTx.Sequence) + // Make sure attempts are loaded into the tx + require.Len(t, stuckTx.TxAttempts, 1) + foundFromAddresses = append(foundFromAddresses, stuckTx.FromAddress) + } + require.Contains(t, foundFromAddresses, fromAddress1) + require.Contains(t, foundFromAddresses, fromAddress2) + }) + + t.Run("excludes transactions already marked for purge", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + mustInsertUnconfirmedEthTxWithBroadcastPurgeAttempt(t, txStore, 0, fromAddress) + stuckTxs, err := stuckTxDetector.FindUnconfirmedTxWithLowestNonce(ctx, []common.Address{fromAddress}) + require.NoError(t, err) + require.Len(t, stuckTxs, 0) + }) +} + +func TestStuckTxDetector_DetectStuckTransactionsHeuristic(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + ethKeyStore := cltest.NewKeyStore(t, db).Eth() + ctx := testutils.Context(t) + + lggr := logger.Test(t) + feeEstimator := gasmocks.NewEvmFeeEstimator(t) + // Return 10 gwei as market gas price + marketGasPrice := tenGwei + fee := gas.EvmFee{Legacy: marketGasPrice} + feeEstimator.On("GetFee", mock.Anything, []byte{}, uint64(0), mock.Anything).Return(fee, uint64(0), nil) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + autoPurgeThreshold := uint32(5) + autoPurgeMinAttempts := uint32(3) + autoPurgeCfg := testAutoPurgeConfig{ + enabled: true, // Enable auto-purge feature for testing + threshold: autoPurgeThreshold, + minAttempts: autoPurgeMinAttempts, + } + blockNum := int64(100) + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), autoPurgeCfg, feeEstimator, txStore, ethClient) + + t.Run("not stuck, Threshold amount of blocks have not passed since broadcast", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + enabledAddresses := []common.Address{fromAddress} + // Create attempts broadcasted at the current broadcast number to test the block num threshold check + // Create autoPurgeMinAttempts number of attempts to ensure the broadcast attempt count check is not being triggered + // Create attempts so that the latest has a higher gas price than the market to ensure the gas price check is not being triggered + mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress, autoPurgeMinAttempts, blockNum, marketGasPrice.Add(oneGwei)) + + // Run detection logic on the same block number as the latest broadcast attempt to stay within the autoPurgeThreshold + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, enabledAddresses, blockNum) + require.NoError(t, err) + require.Len(t, txs, 0) + }) + + t.Run("not stuck, Threshold amount of blocks have not passed since last purge", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + enabledAddresses := []common.Address{fromAddress} + // Create attempts broadcasted autoPurgeThreshold block ago to ensure broadcast block num check is not being triggered + // Create autoPurgeMinAttempts number of attempts to ensure the broadcast attempt count check is not being triggered + // Create attempts so that the latest has a higher gas price than the market to ensure the gas price check is not being triggered + mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress, autoPurgeMinAttempts, blockNum-int64(autoPurgeThreshold), marketGasPrice.Add(oneGwei)) + + // Set the last purge block num as the current block num to test rate limiting condition + stuckTxDetector.SetPurgeBlockNum(fromAddress, blockNum) + + // Run detection logic on autoPurgeThreshold blocks past the latest broadcast attempt + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, enabledAddresses, blockNum) + require.NoError(t, err) + require.Len(t, txs, 0) + }) + + t.Run("not stuck, MinAttempts amount of attempts have not been broadcasted", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + enabledAddresses := []common.Address{fromAddress} + // Create attempts broadcasted autoPurgeThreshold block ago to ensure broadcast block num check is not being triggered + // Create fewer attempts than autoPurgeMinAttempts to test min attempt check + // Create attempts so that the latest has a higher gas price than the market to ensure the gas price check is not being triggered + mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress, autoPurgeMinAttempts-1, blockNum-int64(autoPurgeThreshold), marketGasPrice.Add(oneGwei)) + + // Run detection logic on autoPurgeThreshold blocks past the latest broadcast attempt + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, enabledAddresses, blockNum) + require.NoError(t, err) + require.Len(t, txs, 0) + }) + + t.Run("not stuck, transaction gas price is lower than market gas price", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + enabledAddresses := []common.Address{fromAddress} + // Create attempts broadcasted autoPurgeThreshold block ago to ensure broadcast block num check is not being triggered + // Create autoPurgeMinAttempts number of attempts to ensure the broadcast attempt count check is not being triggered + // Create attempts so that the latest has a lower gas price than the market to test the gas price check + mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress, autoPurgeMinAttempts, blockNum-int64(autoPurgeThreshold), marketGasPrice.Sub(oneGwei)) + + // Run detection logic on autoPurgeThreshold blocks past the latest broadcast attempt + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, enabledAddresses, blockNum) + require.NoError(t, err) + require.Len(t, txs, 0) + }) + + t.Run("detects stuck transaction", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + enabledAddresses := []common.Address{fromAddress} + // Create attempts so that the oldest broadcast attempt's block num is what meets the threshold check + // Create autoPurgeMinAttempts number of attempts to ensure the broadcast attempt count check is not being triggered + // Create attempts broadcasted autoPurgeThreshold block ago to ensure broadcast block num check is not being triggered + mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress, autoPurgeMinAttempts, blockNum-int64(autoPurgeThreshold)+int64(autoPurgeMinAttempts-1), marketGasPrice.Add(oneGwei)) + + // Run detection logic on autoPurgeThreshold blocks past the latest broadcast attempt + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, enabledAddresses, blockNum) + require.NoError(t, err) + require.Len(t, txs, 1) + }) +} + +func TestStuckTxDetector_DetectStuckTransactionsZkEVM(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + ethKeyStore := cltest.NewKeyStore(t, db).Eth() + ctx := testutils.Context(t) + + lggr := logger.Test(t) + feeEstimator := gasmocks.NewEvmFeeEstimator(t) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + autoPurgeCfg := testAutoPurgeConfig{ + enabled: true, + } + blockNum := int64(100) + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, commonconfig.ChainZkEvm, assets.NewWei(assets.NewEth(100).ToInt()), autoPurgeCfg, feeEstimator, txStore, ethClient) + t.Run("returns empty list if no stuck transactions identified", func(t *testing.T) { + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + tx := mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress, 1, blockNum, tenGwei) + attempts := tx.TxAttempts[0] + // Request still returns transaction by hash, transaction not discarded by network and not considered stuck + ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { + return len(b) == 1 && cltest.BatchElemMatchesParams(b[0], attempts.Hash, "eth_getTransactionByHash") + })).Return(nil).Run(func(args mock.Arguments) { + elems := args.Get(1).([]rpc.BatchElem) + resp, err := json.Marshal(types.Transaction{}) + require.NoError(t, err) + elems[0].Error = json.Unmarshal(resp, elems[0].Result) + }).Once() + + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, []common.Address{fromAddress}, blockNum) + require.NoError(t, err) + require.Len(t, txs, 0) + }) + + t.Run("returns stuck transactions discarded by chain", func(t *testing.T) { + // Insert tx that will be mocked as stuck + _, fromAddress1 := cltest.MustInsertRandomKey(t, ethKeyStore) + mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress1, 1, blockNum, tenGwei) + + // Insert tx that will still be valid + _, fromAddress2 := cltest.MustInsertRandomKey(t, ethKeyStore) + mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress2, 1, blockNum, tenGwei) + + // Return nil response for a tx and a normal response for the other + ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { + return len(b) == 2 + })).Return(nil).Run(func(args mock.Arguments) { + elems := args.Get(1).([]rpc.BatchElem) + elems[0].Result = nil // Return nil to signal discarded tx + resp, err := json.Marshal(types.Transaction{}) + require.NoError(t, err) + elems[1].Error = json.Unmarshal(resp, elems[1].Result) // Return non-nil result to signal a valid tx + }).Once() + + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, []common.Address{fromAddress1, fromAddress2}, blockNum) + require.NoError(t, err) + // Expect only 1 tx to return as stuck due to nil eth_getTransactionByHash response + require.Len(t, txs, 1) + }) +} + +func TestStuckTxDetector_DetectStuckTransactionsScroll(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + ethKeyStore := cltest.NewKeyStore(t, db).Eth() + ctx := testutils.Context(t) + + lggr := logger.Test(t) + feeEstimator := gasmocks.NewEvmFeeEstimator(t) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + blockNum := int64(100) + + t.Run("returns stuck tx identified using the custom scroll API", func(t *testing.T) { + // Insert tx that will be mocked as stuck + _, fromAddress1 := cltest.MustInsertRandomKey(t, ethKeyStore) + tx1 := mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress1, 1, blockNum, tenGwei) + attempts1 := tx1.TxAttempts[0] + + // Insert tx that will still be valid + _, fromAddress2 := cltest.MustInsertRandomKey(t, ethKeyStore) + tx2 := mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, 0, fromAddress2, 1, blockNum, tenGwei) + attempts2 := tx2.TxAttempts[0] + + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, err := res.Write([]byte(fmt.Sprintf(`{"errcode": 0,"errmsg": "","data": {"%s": 1, "%s": 0}}`, attempts1.Hash, attempts2.Hash))) + require.NoError(t, err) + })) + defer func() { testServer.Close() }() + testUrl, err := url.Parse(testServer.URL) + require.NoError(t, err) + + autoPurgeCfg := testAutoPurgeConfig{ + enabled: true, + detectionApiUrl: testUrl, + } + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, commonconfig.ChainScroll, assets.NewWei(assets.NewEth(100).ToInt()), autoPurgeCfg, feeEstimator, txStore, ethClient) + + txs, err := stuckTxDetector.DetectStuckTransactions(ctx, []common.Address{fromAddress1, fromAddress2}, blockNum) + require.NoError(t, err) + require.Len(t, txs, 1) + require.Equal(t, tx1.ID, txs[0].ID) + }) +} + +func mustInsertUnconfirmedTxWithBroadcastAttempts(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address, numAttempts uint32, latestBroadcastBlockNum int64, latestGasPrice *assets.Wei) txmgr.Tx { + ctx := testutils.Context(t) + etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, nonce, fromAddress) + // Insert attempts from oldest to newest + for i := int64(numAttempts - 1); i >= 0; i-- { + blockNum := latestBroadcastBlockNum - i + attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) + + attempt.State = txmgrtypes.TxAttemptBroadcast + attempt.BroadcastBeforeBlockNum = &blockNum + attempt.TxFee = gas.EvmFee{Legacy: latestGasPrice.Sub(assets.NewWeiI(i))} + require.NoError(t, txStore.InsertTxAttempt(ctx, &attempt)) + } + etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) + require.NoError(t, err) + return etx +} + +func mustInsertFatalErrorTxWithError(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address, blockNum int64) txmgr.Tx { + etx := cltest.NewEthTx(fromAddress) + etx.State = txmgrcommon.TxFatalError + etx.Error = null.StringFrom("fatal error") + broadcastAt := time.Now() + etx.BroadcastAt = &broadcastAt + etx.InitialBroadcastAt = &broadcastAt + n := types.Nonce(nonce) + etx.Sequence = &n + etx.ChainID = testutils.FixtureChainID + require.NoError(t, txStore.InsertTx(testutils.Context(t), &etx)) + + attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) + ctx := testutils.Context(t) + attempt.State = txmgrtypes.TxAttemptBroadcast + attempt.IsPurgeAttempt = true + require.NoError(t, txStore.InsertTxAttempt(ctx, &attempt)) + + receipt := newTxReceipt(attempt.Hash, int(blockNum), 0) + _, err := txStore.InsertReceipt(ctx, &receipt) + require.NoError(t, err) + + etx, err = txStore.FindTxWithAttempts(ctx, etx.ID) + require.NoError(t, err) + return etx +} + +func mustInsertUnconfirmedEthTxWithBroadcastPurgeAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address) txmgr.Tx { + etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, nonce, fromAddress) + attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) + ctx := testutils.Context(t) + + attempt.State = txmgrtypes.TxAttemptBroadcast + attempt.IsPurgeAttempt = true + require.NoError(t, txStore.InsertTxAttempt(ctx, &attempt)) + etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) + require.NoError(t, err) + return etx +} + +type testAutoPurgeConfig struct { + enabled bool + threshold uint32 + minAttempts uint32 + detectionApiUrl *url.URL +} + +func (t testAutoPurgeConfig) Enabled() bool { return t.enabled } +func (t testAutoPurgeConfig) Threshold() uint32 { return t.threshold } +func (t testAutoPurgeConfig) MinAttempts() uint32 { return t.minAttempts } +func (t testAutoPurgeConfig) DetectionApiUrl() *url.URL { return t.detectionApiUrl } diff --git a/core/chains/evm/txmgr/test_helpers.go b/core/chains/evm/txmgr/test_helpers.go index 64d23373282..ea19e056431 100644 --- a/core/chains/evm/txmgr/test_helpers.go +++ b/core/chains/evm/txmgr/test_helpers.go @@ -1,6 +1,7 @@ package txmgr import ( + "net/url" "testing" "time" @@ -48,16 +49,22 @@ type TestEvmConfig struct { ResendAfterThreshold time.Duration BumpThreshold uint64 MaxQueued uint64 + Enabled bool + Threshold uint32 + MinAttempts uint32 + DetectionApiUrl *url.URL } func (e *TestEvmConfig) Transactions() evmconfig.Transactions { - return &transactionsConfig{e: e} + return &transactionsConfig{e: e, autoPurge: &autoPurgeConfig{}} } func (e *TestEvmConfig) NonceAutoSync() bool { return true } func (e *TestEvmConfig) FinalityDepth() uint32 { return 42 } +func (e *TestEvmConfig) ChainType() commonconfig.ChainType { return "" } + type TestGasEstimatorConfig struct { bumpThreshold uint64 } @@ -115,15 +122,23 @@ func (b *TestBlockHistoryConfig) TransactionPercentile() uint16 { return 42 type transactionsConfig struct { evmconfig.Transactions - e *TestEvmConfig + e *TestEvmConfig + autoPurge evmconfig.AutoPurgeConfig +} + +func (*transactionsConfig) ForwardersEnabled() bool { return true } +func (t *transactionsConfig) MaxInFlight() uint32 { return t.e.MaxInFlight } +func (t *transactionsConfig) MaxQueued() uint64 { return t.e.MaxQueued } +func (t *transactionsConfig) ReaperInterval() time.Duration { return t.e.ReaperInterval } +func (t *transactionsConfig) ReaperThreshold() time.Duration { return t.e.ReaperThreshold } +func (t *transactionsConfig) ResendAfterThreshold() time.Duration { return t.e.ResendAfterThreshold } +func (t *transactionsConfig) AutoPurge() evmconfig.AutoPurgeConfig { return t.autoPurge } + +type autoPurgeConfig struct { + evmconfig.AutoPurgeConfig } -func (*transactionsConfig) ForwardersEnabled() bool { return true } -func (t *transactionsConfig) MaxInFlight() uint32 { return t.e.MaxInFlight } -func (t *transactionsConfig) MaxQueued() uint64 { return t.e.MaxQueued } -func (t *transactionsConfig) ReaperInterval() time.Duration { return t.e.ReaperInterval } -func (t *transactionsConfig) ReaperThreshold() time.Duration { return t.e.ReaperThreshold } -func (t *transactionsConfig) ResendAfterThreshold() time.Duration { return t.e.ResendAfterThreshold } +func (a *autoPurgeConfig) Enabled() bool { return false } type MockConfig struct { EvmConfig *TestEvmConfig diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 85d25d8a70b..b0823c99705 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -641,7 +641,7 @@ func mustInsertConfirmedEthTxBySaveFetchedReceipts(t *testing.T, txStore txmgr.T BlockNumber: big.NewInt(nonce), TransactionIndex: uint(1), } - err := txStore.SaveFetchedReceipts(testutils.Context(t), []*evmtypes.Receipt{&receipt}, &chainID) + err := txStore.SaveFetchedReceipts(testutils.Context(t), []*evmtypes.Receipt{&receipt}, txmgrcommon.TxConfirmed, nil, &chainID) require.NoError(t, err) return etx } diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index 7c9c025d4be..780105d9868 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -659,8 +659,9 @@ func (s *Shell) RebroadcastTransactions(c *cli.Context) (err error) { txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), chain.Config().EVM().GasEstimator(), keyStore.Eth(), nil) cfg := txmgr.NewEvmTxmConfig(chain.Config().EVM()) feeCfg := txmgr.NewEvmTxmFeeConfig(chain.Config().EVM().GasEstimator()) + stuckTxDetector := txmgr.NewStuckTxDetector(lggr, ethClient.ConfiguredChainID(), "", assets.NewWei(assets.NewEth(100).ToInt()), chain.Config().EVM().Transactions().AutoPurge(), nil, orm, ethClient) ec := txmgr.NewEvmConfirmer(orm, txmgr.NewEvmTxmClient(ethClient, chain.Config().EVM().NodePool().Errors()), - cfg, feeCfg, chain.Config().EVM().Transactions(), app.GetConfig().Database(), keyStore.Eth(), txBuilder, chain.Logger()) + cfg, feeCfg, chain.Config().EVM().Transactions(), app.GetConfig().Database(), keyStore.Eth(), txBuilder, chain.Logger(), stuckTxDetector) totalNonces := endingNonce - beginningNonce + 1 nonces := make([]evmtypes.Nonce, totalNonces) for i := int64(0); i < totalNonces; i++ { diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 49360caad21..a4cf1ad7411 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -112,6 +112,16 @@ ReaperThreshold = '168h' # Default # ResendAfterThreshold controls how long to wait before re-broadcasting a transaction that has not yet been confirmed. ResendAfterThreshold = '1m' # Default +[EVM.Transactions.AutoPurge] +# Enabled enables or disables automatically purging transactions that have been idenitified as terminally stuck (will never be included on-chain). This feature is only expected to be used by ZK chains. +Enabled = false # Default +# DetectionApiUrl configures the base url of a custom endpoint used to identify terminally stuck transactions. +DetectionApiUrl = 'https://example.api.io' # Example +# Threshold configures the number of blocks a transaction has to remain unconfirmed before it is evaluated for being terminally stuck. This threshold is only applied if there is no custom API to identify stuck transactions provided by the chain. +Threshold = 5 # Example +# MinAttempts configures the minimum number of broadcasted attempts a transaction has to have before it is evaluated further for being terminally stuck. This threshold is only applied if there is no custom API to identify stuck transactions provided by the chain. Ensure the gas estimator configs take more bump attempts before reaching the configured max gas price. +MinAttempts = 3 # Example + [EVM.BalanceMonitor] # Enabled balance monitoring for all keys. Enabled = true # Default diff --git a/core/config/docs/docs_test.go b/core/config/docs/docs_test.go index 1f76eedcc67..fd59edbab6a 100644 --- a/core/config/docs/docs_test.go +++ b/core/config/docs/docs_test.go @@ -87,6 +87,11 @@ func TestDoc(t *testing.T) { docDefaults.ChainWriter.ForwarderAddress = nil docDefaults.NodePool.Errors = evmcfg.ClientErrors{} + // Transactions.AutoPurge configs are only set if the feature is enabled + docDefaults.Transactions.AutoPurge.DetectionApiUrl = nil + docDefaults.Transactions.AutoPurge.Threshold = nil + docDefaults.Transactions.AutoPurge.MinAttempts = nil + assertTOML(t, fallbackDefaults, docDefaults) }) diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 8119021b565..2aa1d26c326 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -566,6 +566,9 @@ func TestConfig_Marshal(t *testing.T) { ReaperThreshold: &minute, ResendAfterThreshold: &hour, ForwardersEnabled: ptr(true), + AutoPurge: evmcfg.AutoPurgeConfig{ + Enabled: ptr(false), + }, }, HeadTracker: evmcfg.HeadTracker{ @@ -987,6 +990,9 @@ ReaperInterval = '1m0s' ReaperThreshold = '1m0s' ResendAfterThreshold = '1h0m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true @@ -1216,6 +1222,15 @@ func TestConfig_full(t *testing.T) { got.EVM[c].Nodes[n].Order = ptr(int32(100)) } } + if got.EVM[c].Transactions.AutoPurge.Threshold == nil { + got.EVM[c].Transactions.AutoPurge.Threshold = ptr(uint32(0)) + } + if got.EVM[c].Transactions.AutoPurge.MinAttempts == nil { + got.EVM[c].Transactions.AutoPurge.MinAttempts = ptr(uint32(0)) + } + if got.EVM[c].Transactions.AutoPurge.DetectionApiUrl == nil { + got.EVM[c].Transactions.AutoPurge.DetectionApiUrl = new(commoncfg.URL) + } } cfgtest.AssertFieldsNotNil(t, got) @@ -1242,7 +1257,7 @@ func TestConfig_Validate(t *testing.T) { - LDAP.RunUserGroupCN: invalid value (): LDAP ReadUserGroupCN can not be empty - LDAP.RunUserGroupCN: invalid value (): LDAP RunUserGroupCN can not be empty - LDAP.ReadUserGroupCN: invalid value (): LDAP ReadUserGroupCN can not be empty - - EVM: 8 errors: + - EVM: 9 errors: - 1.ChainID: invalid value (1): duplicate - must be unique - 0.Nodes.1.Name: invalid value (foo): duplicate - must be unique - 3.Nodes.4.WSURL: invalid value (ws://dupe.com): duplicate - must be unique @@ -1260,11 +1275,14 @@ func TestConfig_Validate(t *testing.T) { - WSURL: missing: required for primary nodes - HTTPURL: missing: required for all nodes - 1.HTTPURL: missing: required for all nodes - - 1: 6 errors: + - 1: 9 errors: - ChainType: invalid value (Foo): must not be set with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Foo): must be one of arbitrum, celo, gnosis, kroma, metis, optimismBedrock, scroll, wemix, xlayer, zksync or omitted + - ChainType: invalid value (Foo): must be one of arbitrum, celo, gnosis, kroma, metis, optimismBedrock, scroll, wemix, xlayer, zkevm, zksync or omitted - HeadTracker.HistoryDepth: invalid value (30): must be equal to or greater than FinalityDepth + - GasEstimator.BumpThreshold: invalid value (0): cannot be 0 if auto-purge feature is enabled for Foo + - Transactions.AutoPurge.Threshold: missing: needs to be set if auto-purge feature is enabled for Foo + - Transactions.AutoPurge.MinAttempts: missing: needs to be set if auto-purge feature is enabled for Foo - GasEstimator: 2 errors: - FeeCapDefault: invalid value (101 wei): must be equal to PriceMax (99 wei) since you are using FixedPrice estimation with gas bumping disabled in EIP1559 mode - PriceMax will be used as the FeeCap for transactions instead of FeeCapDefault - PriceMax: invalid value (1 gwei): must be greater than or equal to PriceDefault @@ -1272,7 +1290,7 @@ func TestConfig_Validate(t *testing.T) { - 2: 5 errors: - ChainType: invalid value (Arbitrum): only "optimismBedrock" can be used with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Arbitrum): must be one of arbitrum, celo, gnosis, kroma, metis, optimismBedrock, scroll, wemix, xlayer, zksync or omitted + - ChainType: invalid value (Arbitrum): must be one of arbitrum, celo, gnosis, kroma, metis, optimismBedrock, scroll, wemix, xlayer, zkevm, zksync or omitted - FinalityDepth: invalid value (0): must be greater than or equal to 1 - MinIncomingConfirmations: invalid value (0): must be greater than or equal to 1 - 3.Nodes: 5 errors: @@ -1293,6 +1311,7 @@ func TestConfig_Validate(t *testing.T) { - 4: 2 errors: - ChainID: missing: required for all chains - Nodes: missing: must have at least one node + - 5.Transactions.AutoPurge.DetectionApiUrl: invalid value (): must be set for scroll - Cosmos: 5 errors: - 1.ChainID: invalid value (Malaga-420): duplicate - must be unique - 0.Nodes.1.Name: invalid value (test): duplicate - must be unique diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index b199ae530f5..356a6d69930 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -291,6 +291,9 @@ ReaperInterval = '1m0s' ReaperThreshold = '1m0s' ResendAfterThreshold = '1h0m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/core/services/chainlink/testdata/config-invalid.toml b/core/services/chainlink/testdata/config-invalid.toml index 7d1ed17c3c2..b53f4b1712d 100644 --- a/core/services/chainlink/testdata/config-invalid.toml +++ b/core/services/chainlink/testdata/config-invalid.toml @@ -54,6 +54,9 @@ ChainID = '1' ChainType = 'Foo' FinalityDepth = 32 +[EVM.Transactions.AutoPurge] +Enabled = true + [EVM.GasEstimator] Mode = 'FixedPrice' BumpThreshold = 0 @@ -99,6 +102,19 @@ WSURL = 'ws://dupe.com' [[EVM]] +[[EVM]] +ChainID = '534352' +ChainType = 'scroll' + +[EVM.Transactions.AutoPurge] +Enabled = true +DetectionApiUrl = '' + +[[EVM.Nodes]] +Name = 'scroll node' +WSURL = 'ws://foo.bar' +HTTPURl = 'http://foo.bar' + [[Cosmos]] ChainID = 'Malaga-420' diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 7aa3bb50b35..25d62801455 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -278,6 +278,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true @@ -369,6 +372,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true @@ -454,6 +460,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index 94ad1237e90..34852bbe74b 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -400,7 +400,7 @@ func (t *OCRContractTracker) LatestBlockHeight(ctx context.Context) (blockheight // care about the block height; we have no way of getting the L1 block // height anyway return 0, nil - case "", config.ChainArbitrum, config.ChainCelo, config.ChainGnosis, config.ChainKroma, config.ChainOptimismBedrock, config.ChainScroll, config.ChainWeMix, config.ChainXLayer, config.ChainZkSync: + case "", config.ChainArbitrum, config.ChainCelo, config.ChainGnosis, config.ChainKroma, config.ChainOptimismBedrock, config.ChainScroll, config.ChainWeMix, config.ChainXLayer, config.ChainZkEvm, config.ChainZkSync: // continue } latestBlockHeight := t.getLatestBlockHeight() diff --git a/core/services/ocrcommon/block_translator.go b/core/services/ocrcommon/block_translator.go index 06fd9941992..7bce661e692 100644 --- a/core/services/ocrcommon/block_translator.go +++ b/core/services/ocrcommon/block_translator.go @@ -21,7 +21,7 @@ func NewBlockTranslator(cfg Config, client evmclient.Client, lggr logger.Logger) switch cfg.ChainType() { case config.ChainArbitrum: return NewArbitrumBlockTranslator(client, lggr) - case "", config.ChainCelo, config.ChainGnosis, config.ChainKroma, config.ChainMetis, config.ChainOptimismBedrock, config.ChainScroll, config.ChainWeMix, config.ChainXLayer, config.ChainZkSync: + case "", config.ChainCelo, config.ChainGnosis, config.ChainKroma, config.ChainMetis, config.ChainOptimismBedrock, config.ChainScroll, config.ChainWeMix, config.ChainXLayer, config.ChainZkEvm, config.ChainZkSync: fallthrough default: return &l1BlockTranslator{} diff --git a/core/store/migrate/migrations/0239_add_purge_column_tx_attempts.sql b/core/store/migrate/migrations/0239_add_purge_column_tx_attempts.sql new file mode 100644 index 00000000000..af23032d02a --- /dev/null +++ b/core/store/migrate/migrations/0239_add_purge_column_tx_attempts.sql @@ -0,0 +1,32 @@ +-- +goose Up +ALTER TABLE evm.tx_attempts ADD COLUMN is_purge_attempt boolean NOT NULL DEFAULT false; +ALTER TABLE evm.txes DROP CONSTRAINT chk_eth_txes_fsm; +ALTER TABLE evm.txes ADD CONSTRAINT chk_eth_txes_fsm CHECK ( + state = 'unstarted'::eth_txes_state AND nonce IS NULL AND error IS NULL AND broadcast_at IS NULL AND initial_broadcast_at IS NULL + OR + state = 'in_progress'::eth_txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NULL AND initial_broadcast_at IS NULL + OR + state = 'fatal_error'::eth_txes_state AND error IS NOT NULL + OR + state = 'unconfirmed'::eth_txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'confirmed'::eth_txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'confirmed_missing_receipt'::eth_txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL +) NOT VALID; +-- +goose Down +ALTER TABLE evm.tx_attempts DROP COLUMN is_purge_attempt; +ALTER TABLE evm.txes DROP CONSTRAINT chk_eth_txes_fsm; +ALTER TABLE evm.txes ADD CONSTRAINT chk_eth_txes_fsm CHECK ( + state = 'unstarted'::eth_txes_state AND nonce IS NULL AND error IS NULL AND broadcast_at IS NULL AND initial_broadcast_at IS NULL + OR + state = 'in_progress'::eth_txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NULL AND initial_broadcast_at IS NULL + OR + state = 'fatal_error'::eth_txes_state AND nonce IS NULL AND error IS NOT NULL + OR + state = 'unconfirmed'::eth_txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'confirmed'::eth_txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'confirmed_missing_receipt'::eth_txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL +) NOT VALID; \ No newline at end of file diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 75fad4d2fc9..2b6d728df95 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -291,6 +291,9 @@ ReaperInterval = '1m0s' ReaperThreshold = '1m0s' ResendAfterThreshold = '1h0m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 7aa3bb50b35..25d62801455 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -278,6 +278,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true @@ -369,6 +372,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true @@ -454,6 +460,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/docs/CONFIG.md b/docs/CONFIG.md index a0e2957cd72..c7b34dea7f2 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1758,6 +1758,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -1843,6 +1846,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -1928,6 +1934,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2013,6 +2022,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2099,6 +2111,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '30s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2184,6 +2199,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2269,6 +2287,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2355,6 +2376,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2440,6 +2464,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2524,6 +2551,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2608,6 +2638,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2693,6 +2726,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2779,6 +2815,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2864,6 +2903,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -2949,6 +2991,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3034,6 +3079,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '3m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3119,6 +3167,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '3m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3204,6 +3255,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3289,6 +3343,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '30s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3374,6 +3431,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3459,6 +3519,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3544,6 +3607,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3630,6 +3696,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '30s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3715,6 +3784,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3799,6 +3871,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3884,6 +3959,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -3946,6 +4024,7 @@ GasLimit = 5400000 AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false +ChainType = 'zkevm' FinalityDepth = 500 FinalityTagEnabled = false LogBackfillBatchSize = 1000 @@ -3968,6 +4047,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '3m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4053,6 +4135,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4138,6 +4223,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4222,6 +4310,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '0s' ResendAfterThreshold = '0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4284,6 +4375,7 @@ GasLimit = 5400000 AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false +ChainType = 'zkevm' FinalityDepth = 500 FinalityTagEnabled = false LogBackfillBatchSize = 1000 @@ -4306,6 +4398,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '3m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4391,6 +4486,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '30s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4453,6 +4551,7 @@ GasLimit = 5400000 AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false +ChainType = 'zkevm' FinalityDepth = 500 FinalityTagEnabled = false LogBackfillBatchSize = 1000 @@ -4475,6 +4574,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '3m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4560,6 +4662,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4644,6 +4749,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4729,6 +4837,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '30s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4814,6 +4925,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4900,6 +5014,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -4985,6 +5102,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5070,6 +5190,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5155,6 +5278,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5240,6 +5366,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5324,6 +5453,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '3m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5408,6 +5540,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '3m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5492,6 +5627,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '3m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5577,6 +5715,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5662,6 +5803,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5746,6 +5890,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5831,6 +5978,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '30s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -5916,6 +6066,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '30s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6002,6 +6155,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6088,6 +6244,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6173,6 +6332,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6258,6 +6420,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6343,6 +6508,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6428,6 +6596,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6513,6 +6684,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '30s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6598,6 +6772,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6683,6 +6860,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[Transactions.AutoPurge] +Enabled = false + [BalanceMonitor] Enabled = true @@ -6971,6 +7151,40 @@ ResendAfterThreshold = '1m' # Default ``` ResendAfterThreshold controls how long to wait before re-broadcasting a transaction that has not yet been confirmed. +## EVM.Transactions.AutoPurge +```toml +[EVM.Transactions.AutoPurge] +Enabled = false # Default +DetectionApiUrl = 'https://example.api.io' # Example +Threshold = 5 # Example +MinAttempts = 3 # Example +``` + + +### Enabled +```toml +Enabled = false # Default +``` +Enabled enables or disables automatically purging transactions that have been idenitified as terminally stuck (will never be included on-chain). This feature is only expected to be used by ZK chains. + +### DetectionApiUrl +```toml +DetectionApiUrl = 'https://example.api.io' # Example +``` +DetectionApiUrl configures the base url of a custom endpoint used to identify terminally stuck transactions. + +### Threshold +```toml +Threshold = 5 # Example +``` +Threshold configures the number of blocks a transaction has to remain unconfirmed before it is evaluated for being terminally stuck. This threshold is only applied if there is no custom API to identify stuck transactions provided by the chain. + +### MinAttempts +```toml +MinAttempts = 3 # Example +``` +MinAttempts configures the minimum number of broadcasted attempts a transaction has to have before it is evaluated further for being terminally stuck. This threshold is only applied if there is no custom API to identify stuck transactions provided by the chain. Ensure the gas estimator configs take more bump attempts before reaching the configured max gas price. + ## EVM.BalanceMonitor ```toml [EVM.BalanceMonitor] diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index feaf546f022..95366d92f54 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -334,6 +334,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index b37fed41150..02b3eaba50d 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -334,6 +334,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 6ae02ab38f4..d83e54018b5 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -334,6 +334,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index df0118bbbbf..065be4222de 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -324,6 +324,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index edb07fd5e4f..8730cb4b0d5 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -331,6 +331,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index 54de3227a9e..7d45a97cc40 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -330,6 +330,9 @@ ReaperInterval = '1h0m0s' ReaperThreshold = '168h0m0s' ResendAfterThreshold = '1m0s' +[EVM.Transactions.AutoPurge] +Enabled = false + [EVM.BalanceMonitor] Enabled = true From 25173fb2a558fc3576c2bbd118f85a9abf22084e Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 27 May 2024 20:05:41 -0500 Subject: [PATCH 11/19] core/chains/evm/config/toml/defaults: remove ineffectual overrides (#13243) --- .../evm/config/toml/defaults/Avalanche_Fuji.toml | 1 - .../evm/config/toml/defaults/Avalanche_Mainnet.toml | 1 - .../evm/config/toml/defaults/BSC_Mainnet.toml | 13 ------------- .../evm/config/toml/defaults/BSC_Testnet.toml | 13 ------------- .../evm/config/toml/defaults/Ethereum_Goerli.toml | 1 - .../evm/config/toml/defaults/Ethereum_Kovan.toml | 1 - .../evm/config/toml/defaults/Ethereum_Mainnet.toml | 1 - .../evm/config/toml/defaults/Ethereum_Rinkeby.toml | 1 - .../evm/config/toml/defaults/Ethereum_Ropsten.toml | 1 - .../evm/config/toml/defaults/Ethereum_Sepolia.toml | 1 - .../evm/config/toml/defaults/Fantom_Mainnet.toml | 4 ---- .../evm/config/toml/defaults/Fantom_Testnet.toml | 1 - .../evm/config/toml/defaults/Gnosis_Chiado.toml | 2 -- .../evm/config/toml/defaults/Gnosis_Mainnet.toml | 2 -- .../evm/config/toml/defaults/Heco_Mainnet.toml | 13 ------------- .../evm/config/toml/defaults/Metis_Mainnet.toml | 4 ---- .../evm/config/toml/defaults/Metis_Rinkeby.toml | 4 ---- .../evm/config/toml/defaults/Metis_Sepolia.toml | 4 ---- .../evm/config/toml/defaults/Polygon_Amoy.toml | 6 ------ .../evm/config/toml/defaults/Polygon_Mainnet.toml | 5 ----- .../evm/config/toml/defaults/Polygon_Mumbai.toml | 6 ------ core/chains/evm/config/toml/defaults/Simulated.toml | 3 --- 22 files changed, 88 deletions(-) diff --git a/core/chains/evm/config/toml/defaults/Avalanche_Fuji.toml b/core/chains/evm/config/toml/defaults/Avalanche_Fuji.toml index 98c4f9b44ea..25e884d02b3 100644 --- a/core/chains/evm/config/toml/defaults/Avalanche_Fuji.toml +++ b/core/chains/evm/config/toml/defaults/Avalanche_Fuji.toml @@ -8,7 +8,6 @@ OCR.ContractConfirmations = 1 RPCBlockQueryDelay = 2 [GasEstimator] -Mode = 'BlockHistory' PriceDefault = '25 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '25 gwei' diff --git a/core/chains/evm/config/toml/defaults/Avalanche_Mainnet.toml b/core/chains/evm/config/toml/defaults/Avalanche_Mainnet.toml index 77ba6e6b76d..5e2d8dd4274 100644 --- a/core/chains/evm/config/toml/defaults/Avalanche_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Avalanche_Mainnet.toml @@ -8,7 +8,6 @@ OCR.ContractConfirmations = 1 RPCBlockQueryDelay = 2 [GasEstimator] -Mode = 'BlockHistory' PriceDefault = '25 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '25 gwei' diff --git a/core/chains/evm/config/toml/defaults/BSC_Mainnet.toml b/core/chains/evm/config/toml/defaults/BSC_Mainnet.toml index 4268cb25da0..384a798e32a 100644 --- a/core/chains/evm/config/toml/defaults/BSC_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/BSC_Mainnet.toml @@ -2,32 +2,19 @@ # Clique offers finality within (N/2)+1 blocks where N is number of signers # There are 21 BSC validators so theoretically finality should occur after 21/2+1 = 11 blocks ChainID = '56' -# Keeping this >> 11 because it's not expensive and gives us a safety margin -FinalityDepth = 50 LinkContractAddress = '0x404460C6A5EdE2D891e8297795264fDe62ADBB75' LogPollInterval = '3s' -MinIncomingConfirmations = 3 NoNewHeadsThreshold = '30s' RPCBlockQueryDelay = 2 -Transactions.ResendAfterThreshold = '1m' - -[BalanceMonitor] -Enabled = true [GasEstimator] PriceDefault = '5 gwei' -PriceMin = '1 gwei' -BumpMin = '5 gwei' # 15s delay since feeds update every minute in volatile situations BumpThreshold = 5 [GasEstimator.BlockHistory] BlockHistorySize = 24 -[HeadTracker] -HistoryDepth = 100 -SamplingInterval = '1s' - [OCR] DatabaseTimeout = '2s' ContractTransmitterTransmitTimeout = '2s' diff --git a/core/chains/evm/config/toml/defaults/BSC_Testnet.toml b/core/chains/evm/config/toml/defaults/BSC_Testnet.toml index f97dafa0648..424669193b5 100644 --- a/core/chains/evm/config/toml/defaults/BSC_Testnet.toml +++ b/core/chains/evm/config/toml/defaults/BSC_Testnet.toml @@ -2,32 +2,19 @@ # Clique offers finality within (N/2)+1 blocks where N is number of signers # There are 21 BSC validators so theoretically finality should occur after 21/2+1 = 11 blocks ChainID = '97' -# Keeping this >> 11 because it's not expensive and gives us a safety margin -FinalityDepth = 50 LinkContractAddress = '0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06' LogPollInterval = '3s' -MinIncomingConfirmations = 3 NoNewHeadsThreshold = '30s' RPCBlockQueryDelay = 2 -Transactions.ResendAfterThreshold = '1m' - -[BalanceMonitor] -Enabled = true [GasEstimator] PriceDefault = '5 gwei' -PriceMin = '1 gwei' -BumpMin = '5 gwei' # 15s delay since feeds update every minute in volatile situations BumpThreshold = 5 [GasEstimator.BlockHistory] BlockHistorySize = 24 -[HeadTracker] -HistoryDepth = 100 -SamplingInterval = '1s' - [OCR] DatabaseTimeout = '2s' ContractTransmitterTransmitTimeout = '2s' diff --git a/core/chains/evm/config/toml/defaults/Ethereum_Goerli.toml b/core/chains/evm/config/toml/defaults/Ethereum_Goerli.toml index 1dffaefef10..66e0bba8ca0 100644 --- a/core/chains/evm/config/toml/defaults/Ethereum_Goerli.toml +++ b/core/chains/evm/config/toml/defaults/Ethereum_Goerli.toml @@ -6,6 +6,5 @@ MinContractPayment = '0.1 link' EIP1559DynamicFees = true [GasEstimator.BlockHistory] -BatchSize = 25 BlockHistorySize = 4 TransactionPercentile = 50 diff --git a/core/chains/evm/config/toml/defaults/Ethereum_Kovan.toml b/core/chains/evm/config/toml/defaults/Ethereum_Kovan.toml index 9beddc1d215..d3361bb373f 100644 --- a/core/chains/evm/config/toml/defaults/Ethereum_Kovan.toml +++ b/core/chains/evm/config/toml/defaults/Ethereum_Kovan.toml @@ -9,6 +9,5 @@ OperatorFactoryAddress = '0x8007e24251b1D2Fc518Eb843A701d9cD21fe0aA3' EIP1559DynamicFees = false [GasEstimator.BlockHistory] -BatchSize = 25 BlockHistorySize = 4 TransactionPercentile = 50 \ No newline at end of file diff --git a/core/chains/evm/config/toml/defaults/Ethereum_Mainnet.toml b/core/chains/evm/config/toml/defaults/Ethereum_Mainnet.toml index fede762a663..2e65cce6330 100644 --- a/core/chains/evm/config/toml/defaults/Ethereum_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Ethereum_Mainnet.toml @@ -7,7 +7,6 @@ OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' EIP1559DynamicFees = true [GasEstimator.BlockHistory] -BatchSize = 25 # EIP-1559 does well on a smaller block history size BlockHistorySize = 4 TransactionPercentile = 50 diff --git a/core/chains/evm/config/toml/defaults/Ethereum_Rinkeby.toml b/core/chains/evm/config/toml/defaults/Ethereum_Rinkeby.toml index 95e1fcaf2d6..75b01bfb03d 100644 --- a/core/chains/evm/config/toml/defaults/Ethereum_Rinkeby.toml +++ b/core/chains/evm/config/toml/defaults/Ethereum_Rinkeby.toml @@ -8,6 +8,5 @@ MinContractPayment = '0.1 link' EIP1559DynamicFees = false [GasEstimator.BlockHistory] -BatchSize = 25 BlockHistorySize = 4 TransactionPercentile = 50 diff --git a/core/chains/evm/config/toml/defaults/Ethereum_Ropsten.toml b/core/chains/evm/config/toml/defaults/Ethereum_Ropsten.toml index 23c5a355922..7ce9b3e8273 100644 --- a/core/chains/evm/config/toml/defaults/Ethereum_Ropsten.toml +++ b/core/chains/evm/config/toml/defaults/Ethereum_Ropsten.toml @@ -6,6 +6,5 @@ MinContractPayment = '0.1 link' EIP1559DynamicFees = true [GasEstimator.BlockHistory] -BatchSize = 25 BlockHistorySize = 4 TransactionPercentile = 50 diff --git a/core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml b/core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml index 27acddeb721..7729b57b030 100644 --- a/core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml +++ b/core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml @@ -6,7 +6,6 @@ MinContractPayment = '0.1 link' EIP1559DynamicFees = true [GasEstimator.BlockHistory] -BatchSize = 25 BlockHistorySize = 4 TransactionPercentile = 50 diff --git a/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml b/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml index c7fb6ba4736..7e76d94278d 100644 --- a/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml @@ -1,13 +1,9 @@ ChainID = '250' LinkContractAddress = '0x6F43FF82CCA38001B6699a8AC47A2d0E66939407' LogPollInterval = '1s' -MinIncomingConfirmations = 3 NoNewHeadsThreshold = '30s' RPCBlockQueryDelay = 2 -[BalanceMonitor] -Enabled = true - [GasEstimator] # Fantom network has been slow to include txs at times when using the BlockHistory estimator, and the recommendation is to use SuggestedPrice mode. Mode = 'SuggestedPrice' diff --git a/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml b/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml index 1e1aab14681..5f24a76c2e7 100644 --- a/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml +++ b/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml @@ -1,7 +1,6 @@ ChainID = '4002' LinkContractAddress = '0xfaFedb041c0DD4fA2Dc0d87a6B0979Ee6FA7af5F' LogPollInterval = '1s' -MinIncomingConfirmations = 3 # Fantom testnet only emits blocks when a new tx is received, so this method of liveness detection is not useful NoNewHeadsThreshold = '0' RPCBlockQueryDelay = 2 diff --git a/core/chains/evm/config/toml/defaults/Gnosis_Chiado.toml b/core/chains/evm/config/toml/defaults/Gnosis_Chiado.toml index 72c4ed13ae0..1b14da2b540 100644 --- a/core/chains/evm/config/toml/defaults/Gnosis_Chiado.toml +++ b/core/chains/evm/config/toml/defaults/Gnosis_Chiado.toml @@ -7,5 +7,3 @@ LogPollInterval = '5s' [GasEstimator] EIP1559DynamicFees = true PriceMax = '500 gwei' -# 15s delay since feeds update every minute in volatile situations -BumpThreshold = 3 diff --git a/core/chains/evm/config/toml/defaults/Gnosis_Mainnet.toml b/core/chains/evm/config/toml/defaults/Gnosis_Mainnet.toml index 6e180c52e39..587f0083b70 100644 --- a/core/chains/evm/config/toml/defaults/Gnosis_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Gnosis_Mainnet.toml @@ -15,5 +15,3 @@ PriceDefault = '1 gwei' PriceMax = '500 gwei' # 1 Gwei is the minimum accepted by the validators (unless whitelisted) PriceMin = '1 gwei' -# 15s delay since feeds update every minute in volatile situations -BumpThreshold = 3 diff --git a/core/chains/evm/config/toml/defaults/Heco_Mainnet.toml b/core/chains/evm/config/toml/defaults/Heco_Mainnet.toml index 8a45ba99208..b3d4074dba4 100644 --- a/core/chains/evm/config/toml/defaults/Heco_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Heco_Mainnet.toml @@ -1,30 +1,17 @@ # Heco uses BSC's settings. ChainID = '128' -FinalityDepth = 50 LinkContractAddress = '0x404460C6A5EdE2D891e8297795264fDe62ADBB75' LogPollInterval = '3s' -MinIncomingConfirmations = 3 NoNewHeadsThreshold = '30s' RPCBlockQueryDelay = 2 -Transactions.ResendAfterThreshold = '1m' - - -[BalanceMonitor] -Enabled = true [GasEstimator] PriceDefault = '5 gwei' -PriceMin = '1 gwei' -BumpMin = '5 gwei' BumpThreshold = 5 [GasEstimator.BlockHistory] BlockHistorySize = 24 -[HeadTracker] -HistoryDepth = 100 -SamplingInterval = '1s' - [OCR] DatabaseTimeout = '2s' ContractTransmitterTransmitTimeout = '2s' diff --git a/core/chains/evm/config/toml/defaults/Metis_Mainnet.toml b/core/chains/evm/config/toml/defaults/Metis_Mainnet.toml index 337138d565e..f67b6ecce0a 100644 --- a/core/chains/evm/config/toml/defaults/Metis_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Metis_Mainnet.toml @@ -11,10 +11,6 @@ OCR.ContractConfirmations = 1 Mode = 'SuggestedPrice' # Metis uses the SuggestedPrice estimator; we don't want to place any limits on the minimum gas price PriceMin = '0' -BumpThreshold = 3 - -[BalanceMonitor] -Enabled = true [GasEstimator.BlockHistory] # Force an error if someone enables the estimator by accident; we never want to run the block history estimator on metisaa diff --git a/core/chains/evm/config/toml/defaults/Metis_Rinkeby.toml b/core/chains/evm/config/toml/defaults/Metis_Rinkeby.toml index 5dccf3719b9..637dbb41203 100644 --- a/core/chains/evm/config/toml/defaults/Metis_Rinkeby.toml +++ b/core/chains/evm/config/toml/defaults/Metis_Rinkeby.toml @@ -5,13 +5,9 @@ MinIncomingConfirmations = 1 NoNewHeadsThreshold = '0' OCR.ContractConfirmations = 1 -[BalanceMonitor] -Enabled = true - [GasEstimator] Mode = 'SuggestedPrice' PriceMin = '0' -BumpThreshold = 3 [GasEstimator.BlockHistory] BlockHistorySize = 0 diff --git a/core/chains/evm/config/toml/defaults/Metis_Sepolia.toml b/core/chains/evm/config/toml/defaults/Metis_Sepolia.toml index d8ddffd77d7..9354b7a7f04 100644 --- a/core/chains/evm/config/toml/defaults/Metis_Sepolia.toml +++ b/core/chains/evm/config/toml/defaults/Metis_Sepolia.toml @@ -5,13 +5,9 @@ MinIncomingConfirmations = 1 NoNewHeadsThreshold = '0' OCR.ContractConfirmations = 1 -[BalanceMonitor] -Enabled = true - [GasEstimator] Mode = 'SuggestedPrice' PriceMin = '0' -BumpThreshold = 3 [GasEstimator.BlockHistory] BlockHistorySize = 0 diff --git a/core/chains/evm/config/toml/defaults/Polygon_Amoy.toml b/core/chains/evm/config/toml/defaults/Polygon_Amoy.toml index c097e2f7e36..6a1687fec48 100644 --- a/core/chains/evm/config/toml/defaults/Polygon_Amoy.toml +++ b/core/chains/evm/config/toml/defaults/Polygon_Amoy.toml @@ -7,16 +7,11 @@ RPCBlockQueryDelay = 10 RPCDefaultBatchSize = 100 [Transactions] -ResendAfterThreshold = '1m' MaxQueued = 5000 -[BalanceMonitor] -Enabled = true - [GasEstimator] EIP1559DynamicFees = true PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' -PriceMin = '1 gwei' BumpMin = '20 gwei' BumpThreshold = 5 @@ -25,7 +20,6 @@ BlockHistorySize = 24 [HeadTracker] HistoryDepth = 2000 -SamplingInterval = '1s' [NodePool] SyncThreshold = 10 diff --git a/core/chains/evm/config/toml/defaults/Polygon_Mainnet.toml b/core/chains/evm/config/toml/defaults/Polygon_Mainnet.toml index c4246bb82be..50057a6893a 100644 --- a/core/chains/evm/config/toml/defaults/Polygon_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Polygon_Mainnet.toml @@ -12,13 +12,9 @@ RPCDefaultBatchSize = 100 [Transactions] # Matic nodes under high mempool pressure are liable to drop txes, we need to ensure we keep sending them -ResendAfterThreshold = '1m' # Since re-orgs on Polygon can be so large, we need a large safety buffer to allow time for the queue to clear down before we start dropping transactions MaxQueued = 5000 -[BalanceMonitor] -Enabled = true - [GasEstimator] # Many Polygon RPC providers set a minimum of 30 GWei on mainnet to prevent spam PriceDefault = '30 gwei' @@ -35,7 +31,6 @@ 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 -SamplingInterval = '1s' [NodePool] SyncThreshold = 10 diff --git a/core/chains/evm/config/toml/defaults/Polygon_Mumbai.toml b/core/chains/evm/config/toml/defaults/Polygon_Mumbai.toml index e3dd2f6c689..ce0f8861de2 100644 --- a/core/chains/evm/config/toml/defaults/Polygon_Mumbai.toml +++ b/core/chains/evm/config/toml/defaults/Polygon_Mumbai.toml @@ -8,16 +8,11 @@ RPCBlockQueryDelay = 10 RPCDefaultBatchSize = 100 [Transactions] -ResendAfterThreshold = '1m' MaxQueued = 5000 -[BalanceMonitor] -Enabled = true - [GasEstimator] PriceDefault = '1 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' -PriceMin = '1 gwei' BumpMin = '20 gwei' BumpThreshold = 5 @@ -26,7 +21,6 @@ BlockHistorySize = 24 [HeadTracker] HistoryDepth = 2000 -SamplingInterval = '1s' [NodePool] SyncThreshold = 10 diff --git a/core/chains/evm/config/toml/defaults/Simulated.toml b/core/chains/evm/config/toml/defaults/Simulated.toml index 3d01429c27c..8dc71a33edd 100644 --- a/core/chains/evm/config/toml/defaults/Simulated.toml +++ b/core/chains/evm/config/toml/defaults/Simulated.toml @@ -8,9 +8,6 @@ NoNewHeadsThreshold = '0s' ReaperThreshold = '0s' ResendAfterThreshold = '0s' -[BalanceMonitor] -Enabled = true - [GasEstimator] Mode = 'FixedPrice' PriceMin = '0' From 40cca2e0fb487f548b8022e4198e6c0c97188dd0 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Tue, 28 May 2024 09:57:08 +0200 Subject: [PATCH 12/19] Add special transmitter for OCR2 feeds (#13323) * Add special transmitter for OCR2 feeds * Add ocr2FeedsTransmitter FromAddress() --- core/services/ocrcommon/transmitter.go | 105 +++++++++++++++++++++++++ core/services/relay/evm/evm.go | 37 ++++++--- 2 files changed, 132 insertions(+), 10 deletions(-) diff --git a/core/services/ocrcommon/transmitter.go b/core/services/ocrcommon/transmitter.go index bba54334c97..bfb007d1b3e 100644 --- a/core/services/ocrcommon/transmitter.go +++ b/core/services/ocrcommon/transmitter.go @@ -3,6 +3,7 @@ package ocrcommon import ( "context" "math/big" + "slices" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" @@ -63,6 +64,51 @@ func NewTransmitter( }, nil } +type txManagerOCR2 interface { + CreateTransaction(ctx context.Context, txRequest txmgr.TxRequest) (tx txmgr.Tx, err error) + GetForwarderForEOAOCR2Feeds(ctx context.Context, eoa, ocr2AggregatorID common.Address) (forwarder common.Address, err error) +} + +type ocr2FeedsTransmitter struct { + ocr2Aggregator common.Address + txManagerOCR2 + transmitter +} + +// NewOCR2FeedsTransmitter creates a new eth transmitter that handles OCR2 Feeds specific logic surrounding forwarders. +// ocr2FeedsTransmitter validates forwarders before every transmission, enabling smooth onchain config changes without job restarts. +func NewOCR2FeedsTransmitter( + txm txManagerOCR2, + fromAddresses []common.Address, + ocr2Aggregator common.Address, + gasLimit uint64, + effectiveTransmitterAddress common.Address, + strategy types.TxStrategy, + checker txmgr.TransmitCheckerSpec, + chainID *big.Int, + keystore roundRobinKeystore, +) (Transmitter, error) { + // Ensure that a keystore is provided. + if keystore == nil { + return nil, errors.New("nil keystore provided to transmitter") + } + + return &ocr2FeedsTransmitter{ + ocr2Aggregator: ocr2Aggregator, + txManagerOCR2: txm, + transmitter: transmitter{ + txm: txm, + fromAddresses: fromAddresses, + gasLimit: gasLimit, + effectiveTransmitterAddress: effectiveTransmitterAddress, + strategy: strategy, + checker: checker, + chainID: chainID, + keystore: keystore, + }, + }, nil +} + func (t *transmitter) CreateEthTransaction(ctx context.Context, toAddress common.Address, payload []byte, txMeta *txmgr.TxMeta) error { roundRobinFromAddress, err := t.keystore.GetRoundRobinAddress(ctx, t.chainID, t.fromAddresses...) if err != nil { @@ -94,3 +140,62 @@ func (t *transmitter) forwarderAddress() common.Address { } return t.effectiveTransmitterAddress } + +func (t *ocr2FeedsTransmitter) CreateEthTransaction(ctx context.Context, toAddress common.Address, payload []byte, txMeta *txmgr.TxMeta) error { + roundRobinFromAddress, err := t.keystore.GetRoundRobinAddress(ctx, t.chainID, t.fromAddresses...) + if err != nil { + return errors.Wrap(err, "skipped OCR transmission, error getting round-robin address") + } + + forwarderAddress, err := t.forwarderAddress(ctx, roundRobinFromAddress, toAddress) + if err != nil { + return err + } + + _, err = t.txm.CreateTransaction(ctx, txmgr.TxRequest{ + FromAddress: roundRobinFromAddress, + ToAddress: toAddress, + EncodedPayload: payload, + FeeLimit: t.gasLimit, + ForwarderAddress: forwarderAddress, + Strategy: t.strategy, + Checker: t.checker, + Meta: txMeta, + }) + + return errors.Wrap(err, "skipped OCR transmission") +} + +// FromAddress for ocr2FeedsTransmitter returns valid forwarder or effectiveTransmitterAddress if forwarders are not set. +func (t *ocr2FeedsTransmitter) FromAddress() common.Address { + roundRobinFromAddress, err := t.keystore.GetRoundRobinAddress(context.Background(), t.chainID, t.fromAddresses...) + if err != nil { + return t.effectiveTransmitterAddress + } + + forwarderAddress, err := t.forwarderAddress(context.Background(), roundRobinFromAddress, t.ocr2Aggregator) + if err != nil || forwarderAddress == (common.Address{}) { + return t.effectiveTransmitterAddress + } + + return forwarderAddress +} + +func (t *ocr2FeedsTransmitter) forwarderAddress(ctx context.Context, eoa, ocr2Aggregator common.Address) (common.Address, error) { + // If effectiveTransmitterAddress is in fromAddresses, then forwarders aren't set. + if slices.Contains(t.fromAddresses, t.effectiveTransmitterAddress) { + return common.Address{}, nil + } + + forwarderAddress, err := t.GetForwarderForEOAOCR2Feeds(ctx, eoa, ocr2Aggregator) + if err != nil { + return common.Address{}, err + } + + // if forwarder address is in fromAddresses, then none of the forwarders are valid + if slices.Contains(t.fromAddresses, forwarderAddress) { + forwarderAddress = common.Address{} + } + + return forwarderAddress, nil +} diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 3a115006a9f..3f965931596 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -566,17 +566,34 @@ func newOnChainContractTransmitter(ctx context.Context, lggr logger.Logger, rarg gasLimit = uint64(*opts.pluginGasLimit) } - transmitter, err := ocrcommon.NewTransmitter( - configWatcher.chain.TxManager(), - fromAddresses, - gasLimit, - effectiveTransmitterAddress, - strategy, - checker, - configWatcher.chain.ID(), - ethKeystore, - ) + var transmitter Transmitter + var err error + switch commontypes.OCR2PluginType(rargs.ProviderType) { + case commontypes.Median: + transmitter, err = ocrcommon.NewOCR2FeedsTransmitter( + configWatcher.chain.TxManager(), + fromAddresses, + common.HexToAddress(rargs.ContractID), + gasLimit, + effectiveTransmitterAddress, + strategy, + checker, + configWatcher.chain.ID(), + ethKeystore, + ) + default: + transmitter, err = ocrcommon.NewTransmitter( + configWatcher.chain.TxManager(), + fromAddresses, + gasLimit, + effectiveTransmitterAddress, + strategy, + checker, + configWatcher.chain.ID(), + ethKeystore, + ) + } if err != nil { return nil, pkgerrors.Wrap(err, "failed to create transmitter") } From ae56e5a66137cff61a2e1355591cf5fbba3a6241 Mon Sep 17 00:00:00 2001 From: Gheorghe Strimtu Date: Tue, 28 May 2024 12:53:30 +0300 Subject: [PATCH 13/19] TT-1205 Investigate and fix failing OCR2 tests in compatibility pipeline (#13334) * TT-1205 Investigate and fix failing OCR2 tests in compatibility pipeline * don't use test binary * remove dependency on build-tests * fixes * use run-tests instead of run-tests-binary * checkout repo * fix path, add smoke * remove debug line * loki logging * remove test_log_collect=true --- .../workflows/client-compatibility-tests.yml | 77 ++++++++----------- integration-tests/smoke/ocr2_test.go | 1 + 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml index e50d64f3dac..1f7280f8e66 100644 --- a/.github/workflows/client-compatibility-tests.yml +++ b/.github/workflows/client-compatibility-tests.yml @@ -46,39 +46,6 @@ jobs: AWS_REGION: ${{ secrets.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - build-tests: - environment: integration - permissions: - id-token: write - contents: read - name: Build Tests Binary - runs-on: ubuntu-latest - steps: - - name: Collect Metrics - id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@dea9b546553cb4ca936607c2267a09c004e4ab3f # v3.0.0 - with: - id: client-compatablility-build-tests - org-id: ${{ secrets.GRAFANA_INTERNAL_TENANT_ID }} - basic-auth: ${{ secrets.GRAFANA_INTERNAL_BASIC_AUTH }} - hostname: ${{ secrets.GRAFANA_INTERNAL_HOST }} - this-job-name: Build Tests Binary - continue-on-error: true - - name: Checkout the repo - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - with: - ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - - name: Build Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@b49a9d04744b0237908831730f8553f26d73a94b # v2.3.17 - with: - test_download_vendor_packages_command: cd ./integration-tests && go mod download - token: ${{ secrets.GITHUB_TOKEN }} - go_mod_path: ./integration-tests/go.mod - go_tags: embed - cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: "true" - binary_name: tests - # End Build Test Dependencies client-compatibility-matrix: @@ -88,7 +55,7 @@ jobs: pull-requests: write id-token: write contents: read - needs: [build-chainlink, build-tests] + needs: [build-chainlink] env: SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 CHAINLINK_COMMIT_SHA: ${{ github.sha }} @@ -150,12 +117,14 @@ jobs: runs-on: ubuntu-latest name: Client Compatibility Test ${{ matrix.name }} steps: - - name: Download Tests Binary - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + - name: Checkout the repo + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: - name: tests + repository: smartcontractkit/chainlink + ref: ${{ github.sha }} - name: Prepare Base64 TOML config env: + RUN_ID: ${{ github.run_id }} SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-client-compatability-${{ matrix.client }}-testnet @@ -165,6 +134,9 @@ jobs: GRAFANA_URL: "http://localhost:8080/primary" GRAFANA_DASHBOARD_URL: "/d/ddf75041-1e39-42af-aa46-361fe4c36e9e/ci-e2e-tests-logs" GRAFANA_BEARER_TOKEN: ${{ secrets.GRAFANA_INTERNAL_URL_SHORTENER_TOKEN }} + LOKI_ENDPOINT: https://${{ secrets.GRAFANA_INTERNAL_HOST }}/loki/api/v1/push + LOKI_TENANT_ID: ${{ secrets.GRAFANA_INTERNAL_TENANT_ID }} + LOKI_BASIC_AUTH: ${{ secrets.GRAFANA_INTERNAL_BASIC_AUTH }} run: | convert_to_toml_array() { local IFS=',' @@ -212,6 +184,17 @@ jobs: environment="$PYROSCOPE_ENVIRONMENT" key_secret="$PYROSCOPE_KEY" + [Logging] + run_id="$RUN_ID" + + [Logging.LogStream] + log_targets=["file","loki"] + + [Logging.Loki] + tenant_id="$LOKI_TENANT_ID" + endpoint="$LOKI_ENDPOINT" + basic_auth_secret="$LOKI_BASIC_AUTH" + [Logging.Grafana] base_url="$GRAFANA_URL" dashboard_url="$GRAFANA_DASHBOARD_URL" @@ -237,22 +220,28 @@ jobs: echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV touch .root_dir - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@b49a9d04744b0237908831730f8553f26d73a94b # v2.3.17 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@b49a9d04744b0237908831730f8553f26d73a94b # v2.3.17 with: - test_command_to_run: ./tests -test.timeout ${{ matrix.timeout }} -test.run ${{ matrix.test }} - binary_name: tests + test_command_to_run: cd ./integration-tests/smoke && go test -timeout ${{ matrix.timeout }} -count=1 -json -test.run ${{ matrix.test }} 2>&1 | tee /tmp/gotest.log | gotestloghelper -ci -singlepackage + test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} - artifacts_location: ./logs + artifacts_name: ${{ matrix.name }}-test-logs + artifacts_location: | + ./integration-tests/smoke/logs/ + /tmp/gotest.log + publish_check_name: ${{ matrix.name }} token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + QA_KUBECONFIG: "" + should_tidy: "false" + go_coverage_src_dir: /var/tmp/go-coverage + go_coverage_dest_dir: ${{ github.workspace }}/.covdata - name: Print failed test summary if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@b49a9d04744b0237908831730f8553f26d73a94b # v2.3.17 diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 0e1a5c96633..7ca7b01fb36 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logstream" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/integration-tests/actions" From 2f6778d91c5d807f39eb7ecbc12f54943ed73eb0 Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Tue, 28 May 2024 15:02:56 +0200 Subject: [PATCH 14/19] Add check for existence of ECR images used as test dependencies in E2E EVM compatibility tests (#13271) * Add check for existence of ECR images used as test dependencies The code changes include adding a new step in the workflow file `evm-version-compatibility-tests.yml` to check if the images used as test dependencies exist in the ECR. This step is triggered when there is a change in the test dependency or when the workflow is manually dispatched. The step checks if the latest Ethereum client image exists in the ECR and fails the workflow if it doesn't exist. * Add CTF workflow trigger job * Add permissions for the trigger job * Add actions: read permission * Refactor workflow * Fix * fix * Use update-internal-mirrors action from CTF to wait until image is updated * Bump action version * Add image check to evm compatibility tests * Bump action version * Check images for other clients * Check only ethereum/client-go in evm-version-compatibility-tests.yml --- .../workflows/client-compatibility-tests.yml | 37 ++++++++++++++++++- .../evm-version-compatibility-tests.yml | 27 ++++++++++++-- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml index 1f7280f8e66..df5c0f7b406 100644 --- a/.github/workflows/client-compatibility-tests.yml +++ b/.github/workflows/client-compatibility-tests.yml @@ -15,6 +15,39 @@ env: jobs: # Build Test Dependencies + check-ecr-images-exist: + environment: integration + permissions: + id-token: write + contents: read + name: Check images used as test dependencies exist in ECR + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + mirror: + - name: ethereum/client-go + expression: '^(alltools-v|v)[0-9]\.[0-9]+\.[0-9]+$' + - name: hyperledger/besu + expression: '^[0-9]+\.[0-9]+(\.[0-9]+)?$' + page_size: 300 + - name: thorax/erigon + expression: '^v[0-9]+\.[0-9]+\.[0-9]+$' + - name: nethermind/nethermind + expression: '^[0-9]+\.[0-9]+\.[0-9]+$' + - name: tofelb/ethereum-genesis-generator + expression: '^[0-9]+\.[0-9]+\.[0-9]+(\-slots\-per\-epoch)?' + steps: + - name: Update internal ECR if the latest Ethereum client image does not exist + uses: smartcontractkit/chainlink-testing-framework/.github/actions/update-internal-mirrors@7eb04a030823b316d8dd5bb555f1e49593a503fc + with: + aws_region: ${{ secrets.QA_AWS_REGION }} + role_to_assume: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + aws_account_number: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + image_name: ${{matrix.mirror.name}} + expression: ${{matrix.mirror.expression}} + page_size: ${{matrix.mirror.page_size}} + build-chainlink: environment: integration permissions: @@ -55,7 +88,7 @@ jobs: pull-requests: write id-token: write contents: read - needs: [build-chainlink] + needs: [build-chainlink, check-ecr-images-exist] env: SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 CHAINLINK_COMMIT_SHA: ${{ github.sha }} @@ -335,4 +368,4 @@ jobs: message_title: ${{ matrix.product }} slack_channel_id: ${{ secrets.QA_SLACK_CHANNEL }} slack_bot_token: ${{ secrets.QA_SLACK_API_KEY }} - slack_thread_ts: ${{ needs.start-slack-thread.outputs.thread_ts }} + slack_thread_ts: ${{ needs.start-slack-thread.outputs.thread_ts }} \ No newline at end of file diff --git a/.github/workflows/evm-version-compatibility-tests.yml b/.github/workflows/evm-version-compatibility-tests.yml index 79173b376bf..f9b60eafab9 100644 --- a/.github/workflows/evm-version-compatibility-tests.yml +++ b/.github/workflows/evm-version-compatibility-tests.yml @@ -18,7 +18,6 @@ env: MOD_CACHE_VERSION: 2 jobs: - # Check if go.mod has changed check-dependency-bump: runs-on: ubuntu-latest @@ -55,11 +54,31 @@ jobs: echo "dependency_changed=false" >> $GITHUB_OUTPUT fi + # Check if images used as test dependencies exist in the ECR + check-ecr-images-exist: + if: needs.check-dependency-bump.outputs.dependency_changed == 'true' || github.event_name == 'workflow_dispatch' + needs: [check-dependency-bump] + environment: integration + permissions: + id-token: write + contents: read + name: Check images used as test dependencies exist in ECR + runs-on: ubuntu-latest + steps: + - name: Update internal ECR if the latest Ethereum client image does not exist + uses: smartcontractkit/chainlink-testing-framework/.github/actions/update-internal-mirrors@7eb04a030823b316d8dd5bb555f1e49593a503fc + with: + aws_region: ${{ secrets.QA_AWS_REGION }} + role_to_assume: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + aws_account_number: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + image_name: 'ethereum/client-go' + expression: '^(alltools-v|v)[0-9]\.[0-9]+\.[0-9]+$' + # Build Test Dependencies build-chainlink: if: needs.check-dependency-bump.outputs.dependency_changed == 'true' || github.event_name == 'workflow_dispatch' - needs: [check-dependency-bump] + needs: [check-dependency-bump, check-ecr-images-exist] environment: integration permissions: id-token: write @@ -92,7 +111,7 @@ jobs: build-tests: if: needs.check-dependency-bump.outputs.dependency_changed == 'true' || github.event_name == 'workflow_dispatch' - needs: [check-dependency-bump] + needs: [check-dependency-bump, check-ecr-images-exist] environment: integration permissions: id-token: write @@ -127,7 +146,7 @@ jobs: build-test-matrix: if: needs.check-dependency-bump.outputs.dependency_changed == 'true' || github.event_name == 'workflow_dispatch' - needs: [check-dependency-bump] + needs: [check-dependency-bump, check-ecr-images-exist] runs-on: ubuntu-latest name: Build Test Matrix outputs: From c3829cac20e158f805d0efb9a8a2183e8d7b0515 Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Tue, 28 May 2024 09:05:32 -0500 Subject: [PATCH 15/19] Add Geth InsufficientEth client error (#13312) * Added client error under Geth InsufficientEth * Added changeset --- .changeset/fuzzy-bobcats-shave.md | 5 +++++ core/chains/evm/client/errors.go | 2 +- core/chains/evm/client/errors_test.go | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/fuzzy-bobcats-shave.md diff --git a/.changeset/fuzzy-bobcats-shave.md b/.changeset/fuzzy-bobcats-shave.md new file mode 100644 index 00000000000..b8996448983 --- /dev/null +++ b/.changeset/fuzzy-bobcats-shave.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#internal Added new Geth InsufficientEth client error for internal TXM classification diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index bcc8ff961f0..5f03ab102d5 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -106,7 +106,7 @@ var geth = ClientErrors{ ReplacementTransactionUnderpriced: regexp.MustCompile(`(: |^)replacement transaction underpriced$`), TransactionAlreadyInMempool: regexp.MustCompile(`(: |^)(?i)(known transaction|already known)`), TerminallyUnderpriced: regexp.MustCompile(`(: |^)transaction underpriced$`), - InsufficientEth: regexp.MustCompile(`(: |^)(insufficient funds for transfer|insufficient funds for gas \* price \+ value|insufficient balance for transfer)$`), + InsufficientEth: regexp.MustCompile(`(: |^)(insufficient funds for transfer|insufficient funds for gas \* price \+ value|insufficient balance for transfer|transaction would cause overdraft)$`), TxFeeExceedsCap: regexp.MustCompile(`(: |^)tx fee \([0-9\.]+ [a-zA-Z]+\) exceeds the configured cap \([0-9\.]+ [a-zA-Z]+\)$`), Fatal: gethFatal, } diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go index bdf7bfbe726..cca54c2a4a9 100644 --- a/core/chains/evm/client/errors_test.go +++ b/core/chains/evm/client/errors_test.go @@ -213,6 +213,7 @@ func Test_Eth_Errors(t *testing.T) { {"insufficient balance for transfer", true, "zkSync"}, {"insufficient funds for gas + value. balance: 42719769622667482000, fee: 48098250000000, value: 42719769622667482000", true, "celo"}, {"client error insufficient eth", true, "tomlConfig"}, + {"transaction would cause overdraft", true, "Geth"}, } for _, test := range tests { err = evmclient.NewSendErrorS(test.message) From 79f4c7dc578427c820c5ea6f06dbf96361e2b456 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Tue, 28 May 2024 16:25:36 +0200 Subject: [PATCH 16/19] [TT-1207] use seth utils from ctf, add support for default network (#13333) * typos, readme update and new Seth-specific readme * add Seth-focused readme * seth readme improvements * more comments in defaul.toml * change heading naming * add info about returning funds and rebalancing * update Seth readme with table of content and one more how-to case * even fresher seth readme * add execution revert to Seth readme * use seth utils from ctf, add support for default network * use tagget Seth, newer CTF * update readme and toml * use tagged CTF, add settings for Arb Sepolia --- integration-tests/LOG_POLLER.md | 18 +- integration-tests/README.md | 4 + integration-tests/README_SETH.md | 464 ++++++++++++++++++ integration-tests/actions/seth/actions.go | 8 +- integration-tests/benchmark/keeper_test.go | 4 +- .../chaos/automation_chaos_test.go | 6 +- integration-tests/chaos/ocr2vrf_chaos_test.go | 4 +- integration-tests/chaos/ocr_chaos_test.go | 8 +- integration-tests/go.mod | 4 +- integration-tests/go.sum | 8 +- .../automationv2_1/automationv2_1_test.go | 4 +- integration-tests/load/go.mod | 4 +- integration-tests/load/go.sum | 8 +- integration-tests/load/vrfv2/gun.go | 7 +- integration-tests/load/vrfv2plus/gun.go | 7 +- integration-tests/smoke/ocr2vrf_test.go | 6 +- integration-tests/testconfig/README.md | 77 +-- integration-tests/testconfig/default.toml | 134 +++-- integration-tests/testconfig/testconfig.go | 9 +- integration-tests/testsetups/ocr.go | 7 +- integration-tests/utils/seth.go | 146 ------ 21 files changed, 617 insertions(+), 320 deletions(-) create mode 100644 integration-tests/README_SETH.md delete mode 100644 integration-tests/utils/seth.go diff --git a/integration-tests/LOG_POLLER.md b/integration-tests/LOG_POLLER.md index 6e98fba5525..3d6f4b78cef 100644 --- a/integration-tests/LOG_POLLER.md +++ b/integration-tests/LOG_POLLER.md @@ -6,11 +6,7 @@ * WASP's `gun` implementation is imperfect in terms of generated load ## Configuration -Due to unfinished migration to TOML config tests use a mixed configuration approach: -* network, RPC endpoints, funding keys, etc need to be provided by env vars -* test-specific configuration can be provided by TOML file or via a `Config` struct (to which TOML is parsed anyway) additionally some of it can be overridden by env vars (for ease of use in CI) -** smoke tests use the programmatical approach -** load test uses the TOML approach +Use TOML to configure all test aspects: load characteristics, RPC endpoints, Chainlink image, etc. ## Approximated test scenario Different tests might have slightly modified scenarios, but generally they follow this pattern: @@ -29,16 +25,6 @@ Different tests might have slightly modified scenarios, but generally they follo All of the checks use fluent waits. -### Required env vars -* `CHAINLINK_IMAGE` -* `CHAINLINK_VERSION` -* `SELECTED_NETWORKS` - -### Env vars required for live testnet tests -* `EVM_WS_URL` -- RPC websocket -* `EVM_HTTP_URL` -- RPC HTTP -* `EVM_KEYS` -- private keys used for funding - Since on live testnets we are using existing and canonical LINK contracts funding keys need to contain enough LINK to pay for the test. There's an automated check that fails during setup if there's not enough LINK. Approximately `9 LINK` is required for each UpKeep contract test uses to register a `LogTrigger`. Test contract emits 3 types of events and unless configured otherwise (programmatically!) all of them will be used, which means that due to Automation's limitation we need to register a separate `LogTrigger` for each event type for each contract. So if you want to test with 100 contracts, then you'd need to register 300 UpKeep contracts and thus your funding address needs to have at least 2700 LINK. ### Programmatical config @@ -111,7 +97,7 @@ For other nuances do check [gun.go][integration-tests/universal/log_poller/gun.g ### TOML config That config follows the same structure as programmatical config shown above. -Sample config: [config.toml](integration-tests/load/log_poller/config.toml) +Sample config: [config.toml](./testconfig/log_poller/log_poller.toml) Use this snippet instead of creating the `Config` struct programmatically: ``` diff --git a/integration-tests/README.md b/integration-tests/README.md index 80154a6dd13..860f0759536 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -49,6 +49,10 @@ Most test files have a couple of tests, it's recommended to look into the file a It's generally recommended to run only one test at a time on a local machine as it needs a lot of docker containers and can peg your resources otherwise. You will see docker containers spin up on your machine for each component of the test where you can inspect logs. +### Configure Seth + +Our new evm client is Seth. Detailed instructions on how to configure it can be found in the [Seth README](./README_SETH.md) in this repo as well as in [Seth repository](https://github.com/smartcontractkit/seth). + ## Analyze You can see the results of each test in the terminal with normal `go test` output. If a test fails, logs of each Chainlink container will dump into the `smoke/logs/` folder for later analysis. You can also see these logs in CI uploaded as GitHub artifacts. diff --git a/integration-tests/README_SETH.md b/integration-tests/README_SETH.md new file mode 100644 index 00000000000..7d0d31674ff --- /dev/null +++ b/integration-tests/README_SETH.md @@ -0,0 +1,464 @@ +# Seth-Specific Instructions + +## Table of Contents +1. [Introduction](#introduction) +2. [How to Set Configuration Values](#how-to-set-configuration-values) + 1. [Example](#example) + 2. [Documentation and Further Details](#documentation-and-further-details) +3. [How to Set Seth Logging Level](#how-to-set-seth-logging-level) + 1. [Locally](#locally) + 2. [Remote Runner](#remote-runner) +4. [How to Set Seth Network Configuration](#how-to-set-seth-network-configuration) + 1. [Overview of Configuration Usage](#overview-of-configuration-usage) + 2. [Seth-Specific Network Configuration](#seth-specific-network-configuration) + 3. [Steps for Adding a New Network](#steps-for-adding-a-new-network) + 1. [Network is Already Defined in known_networks.go](#network-is-already-defined-in-known_networksgo) + 2. [It's a New Network](#its-a-new-network) +5. [How to Use Seth CLI](#how-to-use-seth-cli) +6. [How to Get Fallback (Hardcoded) Values](#how-to-get-fallback-hardcoded-values) + 1. [Steps to Use Seth CLI for Fallback Values](#steps-to-use-seth-cli-for-fallback-values) +7. [Ephemeral and Static Keys Explained](#ephemeral-and-static-keys-explained) + 1. [Understanding the Keys](#understanding-the-keys) + 2. [How to Set Ephemeral Keys in the TOML](#how-to-set-ephemeral-keys-in-the-toml) + 3. [How to Set Static Keys in the TOML](#how-to-set-static-keys-in-the-toml) + 1. [As a List of Wallet Keys in Your Network Configuration](#as-a-list-of-wallet-keys-in-your-network-configuration) + 2. [As Base64-Encoded Keyfile Stored as GHA Secret](#as-base64-encoded-keyfile-stored-as-gha-secret) +8. [How to Split Funds Between Static Keys](#how-to-split-funds-between-static-keys) +9. [How to Return Funds From Static Keys to the Root Key](#how-to-return-funds-from-static-keys-to-the-root-key) + 1. [How to Rebalance or Top Up Static Keys](#how-to-rebalance-or-top-up-static-keys) +10. [How to Deal with "TX Fee Exceeds the Configured Cap" Error](#how-to-deal-with-tx-fee-exceeds-the-configured-cap-error) +11. [How to Use Seth's Synchronous API](#how-to-use-seths-synchronous-api) +12. [How to Read Event Data from Transactions](#how-to-read-event-data-from-transactions) +13. [How to Deal with Failed Transactions](#how-to-deal-with-failed-transactions) +14. [How Automated Gas Estimation Works](#how-automated-gas-estimation-works) + 1. [Legacy Transactions](#legacy-transactions) + 2. [EIP-1559 Transactions](#eip-1559-transactions) + 3. [Adjustment Factor](#adjustment-factor) + 4. [Buffer Percents](#buffer-percents) +18. [How to Tweak Automated Gas Estimation](#how-to-tweak-automated-gas-estimation) +19. [How to debug with 'execution reverted' error](#how-to-debug-with-execution-reverted-error) +20. [How to split non-native tokens between keys](#how-to-split-non-native-tokens-between-keys) + +## Introduction + +[Seth](https://github.com/smartcontractkit/seth) is the Ethereum client we use for integration tests. It is designed to be a thin wrapper over `go-ethereum` client that adds a couple of key features: +* key management +* transaction decoding and tracing +* gas estimation + +To use it you don't need to add any specific configuration to your TOML files. Reasonable defaults have been added to `default.toml` file under `[Seth]` section. For some of the products +we have added a couple of product-scoped overrides. For example for Automation's Load tests we have increased ephemeral addresses count from `10` to `100`: +```toml +[Load.Seth] +ephemeral_addresses_number = 100 +``` + +Feel free to modify the configuration to suit your needs, but remember to scope it correctly, so that it doesn't impact other products. You can find more information about TOML configuration and override precedences [here](./testconfig/README.md). + +## How to Set Configuration Values +Place all Seth-specific configuration entries under the `[Seth]` section in your TOML file. This can be done in files such as `default.toml` or `overrides.toml` or any product-specific TOML located in the [testconfig](./testconfig) folder. + +### Example: +```toml +[Seth] +tracing_level = "all" # trace all transactions regardless of whether they are reverted or not +``` + +### Documentation and Further Details +For a comprehensive description of all available configuration options, refer to the `[Seth]` section of configuration documentation in the [default.toml](./testconfig/default.toml) file or consult the Seth [README.md on GitHub](https://github.com/smartcontractkit/seth/blob/master/README.md). + +## How to set Seth logging level +### Locally +To adjust the logging level for Seth when running locally, use the environment variable `SETH_LOG_LEVEL`. For basic tracing and decoding information, set this variable to `debug`. For more detailed tracing, use the `trace` level. +### Remote Runner +To set the Seth log level in the Remote Runner, use the `TEST_SETH_LOG_LEVEL` environment variable. In the future, we plan to implement automatic forwarding of the `SETH_LOG_LEVEL` environment variable. Until then, you must set it explicitly. + +## How to set Seth Network Configuration +Seth's network configuration is entirely separate from the traditional `EVMNetwork`, and the two cannot be used interchangeably. Currently, both configurations must be provided for tests to function properly, as different parts of the test utilize each configuration. + +### Overview of Configuration Usage +While most of the test logic relies on the `EVMNetwork` struct, Seth employs its own network configuration. To facilitate ease of use, we have introduced convenience methods that duplicate certain fields from `EVMNetwork` to `seth.Network`, eliminating the need to specify the same values twice. The following fields are automatically copied: + +- Private keys +- RPC endpoints +- EIP-1559 support (only for simulated networks) +- Default gas limit (only for simulated networks) + +### Seth-Specific Network Configuration +Since version v1.0.11 Seth will use a default network configuration if you don't provide one in your TOML file. It is stored in `default.toml` and named `Default`. It has both EIP-1559 and gas estimation enabled, because Seth will disable both if they are not supported by the network. +If for whatever reason you want to use different default settings for you product, please add them to your product-specific TOML file. + +You can still provide your own network configuration in your TOML file, if you need to override the default settings. When you do that, you need to provide all the following fields: +- Fallback gas price +- Fallback gas tip/fee cap +- Fallback gas limit (used for contract deployment and interaction) +- Fallback transfer fee (used for transferring funds between accounts) +- Network name +- Transaction timeout + +### Steps for adding a new network + +#### Network is already defined in [known_networks.go](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/networks/known_networks.go) +If you are fine with the default network configuration, you don't need to do anything. Otherwise, you need add only Seth-specific network configuration to `[[Seth.networks]]` table. Here's an example: +```toml +[[Seth.networks]] +name = "ARBITRUM_SEPOLIA" +transaction_timeout = "10m" +transfer_gas_fee = 50_000 +# gas_limit = 15_000_000 +# legacy transactions fallback gas price +gas_price = 200_000_000 +# EIP-1559 transactions fallback gas tip cap and fee cap +eip_1559_dynamic_fees = true +gas_fee_cap = 400_000_000 +gas_tip_cap = 200_000_000 +# if set to true we will estimate gas for every transaction +gas_price_estimation_enabled = true +# how many last blocks to use, when estimating gas for a transaction +gas_price_estimation_blocks = 100 +# priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor will be used for gas estimation) [default: "standard"] +gas_price_estimation_tx_priority = "standard" +``` + +Name of the network doesn't really matter and is used only for logging purposes. Chain ID must match the one from `known_networks.go` file. + +**Warning!** Please do not use the values from above-mentioned example. They should be replaced with the actual values obtained from gas tracker or Seth CLI (more on that later). + +#### It's a new network +Apart from above-mentioned fields you either need to add the network to `known_networks.go` file in the [CTF](https://github.com/smartcontractkit/chainlink-testing-framework) or define it in your test TOML file. +Here's an example of how to define a new `EVMNetwork` network in your test TOML file: +```toml +[Network.EVMNetworks.ARBITRUM_SEPOLIA] +evm_name = "ARBITRUM_SEPOLIA" +evm_urls = ["rpc ws endpoint"] +evm_http_urls = ["rpc http endpoint"] +client_implementation = "Ethereum" +evm_keys = ["private keys you want to use"] +evm_simulated = false +evm_chainlink_transaction_limit = 5000 +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 10000 +evm_supports_eip1559 = false +evm_default_gas_limit = 6000000 +``` + +### Things to remember: +* you need **both** networks: one for EVM and one for Seth (unless you are fine with the default settings) +* websocket URL and private keys from the `EVMNetwork` will be copied over to the `Seth.Network` configuration, so you don't need to provide them again +* it's advised to not set the gas limit, unless your test fails without it (might happen when interacting with new networks due bugs or gas estimation quirks); Seth will try to estimate gas for each interaction +* **name** of `Seth.Network` must match the one from `EVMNetwork` configuration + +While this covers the essentials, it is advisable to consult the Seth documentation for detailed settings related to gas estimation, tracing, etc. + +## How to use Seth CLI +The most important thing to keep in mind that the CLI requires you to provide a couple of settings via environment variables, in addition to a TOML configuration file. Here's a general breakdown of the required settings: +* `keys` commands requires `SETH_KEYFILE_PATH`, `SETH_CONFIG_PATH` and `SETH_ROOT_PRIVATE_KEY` environment variables +* `gas` and `stats` command requires `SETH_CONFIG_PATH` environment variable + +You can find a sample `Seth.toml` file [here](https://github.com/smartcontractkit/seth/blob/master/seth.toml). Currently, you cannot use your test TOML file as a Seth configuration file, but we will add ability that in the future. + +## How to get Fallback (Hardcoded) Values +There are two primary methods to obtain fallback values for network configuration: +1. **Web-Based Gas Tracker**: Model fallback values based on historical gas prices. +2. **Seth CLI**: This method is more complex, but works for any network. We will focus on it due to its broad applicability. + +### Steps to Use Seth CLI for Fallback Values +1. **Clone the Seth Repository:** + Clone the repository from GitHub using: +```bash +git clone https://github.com/smartcontractkit/seth +``` + +2. **Run Seth CLI:** + Execute the command to get fallback gas prices: +```bash +SETH_CONFIG_PATH=seth.toml go run cmd/seth/seth.go -u https://RPC_TO_USE -b 10000 -tp 0.99 +``` +The network name passed in the CLI must match the one in your TOML file (it is case-sensitive). The `-b` flag specifies the number of blocks to consider for gas estimation, and `-tp` denotes the tip percentage. + +3**Copy Fallback Values:** + From the output, copy the relevant fallback prices into your network configuration in test TOML. Here's an example of what you might see: +```bash + 5:08PM INF Fallback prices for TOML config: +gas_price = 121487901046 +gas_tip_cap = 1000000000 +gas_fee_cap = 122487901046 +``` + +5. **Update TOML Configuration:** + Update your network configuration with the copied values: +```toml +[[Seth.networks]] +name = "my_network" +transaction_timeout = "10m" +transfer_gas_fee = 21_000 +eip_1559_dynamic_fees = true +gas_fee_cap = 122487901046 +gas_tip_cap = 1000000000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 100 +gas_price_estimation_tx_priority = "standard" +``` + +This method ensures you get the most accurate and network-specific fallback values, optimizing your setup for current network conditions. + +## Ephemeral and Static Keys explained +Understanding the difference between ephemeral and static keys is essential for effective and safe use of Seth. + +### Understanding the Keys +- **Ephemeral Keys**: These are generated on the fly, are not stored, and should not be used on live networks, because any funds associated will be lost. Use these keys only when it's acceptable to lose the funds. +- **Static Keys**: These are specified in the Seth configuration and are suitable for use on live networks. Funds associated with these keys are not lost post-test since you retain copies of the private keys. + +Here are a couple of use cases where you might need to use ephemeral keys or more than one static key: + +- **Parallel Operations**: If you need to run multiple operations simultaneously without waiting for the previous one to finish, remember that Seth is synchronous and requires different keys for each goroutine. +- **Load Generation**: To generate a large volume of transactions in a short time. + +Most tests, especially on live networks, will restrict the use of ephemeral keys. + +### How to Set Ephemeral Keys in the TOML +Setting ephemeral keys is straightforward: +```toml +[Seth] +ephemeral_addresses_number = 10 +``` + +### How to Set Static Keys in the TOML +There are several methods to set static keys, but here are two: + +#### As a List of Wallet Keys in Your Network Configuration +Add it directly to your test TOML: +```toml +[Network.WalletKeys] +arbitrum_sepolia=["first_key","second_key"] +``` +This method is ideal for local tests, but should be avoided in continuous integration (CI) environments. + +#### As Base64-Encoded Keyfile Stored as GHA Secret +This safer, preferred method involves more setup: + +1. **Configuration**: Your Seth must be configured to read the keyfile in Base64-encoded version from an environment variable, by setting in your TOML: +``` +[Seth] +keyfile_source = "base64_env" +``` +2. **Pipeline Setup**: Your pipeline must have the secret with the Base64-encoded keyfile exposed as an environment variable named `SETH_KEYFILE_BASE64`. Seth will automatically read and decode it given the above-mentioned configuration. + +### Setting Up Your Pipeline +Here's how to add the keyfile to your GitHub Actions secrets: +1. Create a keyfile (instructions provided below). +2. Base64-encode the keyfile and add it to your GitHub Actions secrets using the GitHub CLI: +``` +gh secret set SETH_MY_NETWORK_KEYFILE_BASE64 -b $(cat keyfile_my_network.toml | base64) +``` + +It is advised to use a separate keyfile for each network to avoid confusion and potential errors. If you need to run your test on multiple networks you should add logic to your pipeline that will set the correct keyfile based on the network you are testing. + +## How to Split Funds Between Static Keys +Managing funds across multiple static keys can be complex, especially if your tests require a substantial number of keys to generate adequate load. To simplify this process, follow these steps: + +1. **Fund a Root Key**: Start by funding a key (referred to as the root key) with the total amount of funds you intend to distribute among other keys. +2. **Use Seth to Distribute Funds**: Execute the command below to split the funds from the root key to other keys: +``` +SETH_KEYFILE_PATH=keyfile_my_network.toml SETH_ROOT_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 SETH_CONFIG_PATH=seth.toml go run cmd/seth/seth.go -n my_network keys fund -a 10 -b 1 +``` +The `-a ` option specifies the number of keys to distribute funds to, and `-b ` denotes the buffer (in ethers) to be left on the root key. + +## How to Return Funds From Static Keys to the Root Key +Returning funds from static keys to the root key is a simple process. Execute the following command: +```bash +KEYFILE_PATH=keyfile_my_network.toml ROOT_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 SETH_CONFIG_PATH=seth.toml go run cmd/seth/seth.go -n my_network keys return +``` +This command will return all funds from static keys read from `keyfile_my_network.toml` to the root key. + +## How to Rebalance or Top Up Static Keys +Currently, Seth doesn't have a single step function for this, but you can follow these steps to rebalance or top up static keys: + +### Rebalancing Static Keys +1. **Return Funds to Root Key**: Use the command `keys return` to transfer all funds back to the root key. +2. **Redistribute Funds**: Use the command `keys funds` to allocate the funds among the keys as needed. + +After rebalancing, upload the new keyfile to the CI as a base64-encoded secret. + +### Topping Up Static Keys +1. **Fund the Root Key**: Add funds to the root key. +2. **Distribute Funds**: Use the `keys fund` command to distribute these funds among the keys. + +The key is to understand that `keys fund` command will use existing keys if a keyfile is found, or generate new private keys and save them if the file doesn't exist. + +For both rebalancing and topping up, you don't need to upload the keyfile to the CI again, as the private keys remain the same, only their on-chain balances change. + +**Tip**: Always keep copies of keyfiles in 1Password to easily restore them if needed. This is crucial for rebalancing because you cannot download the keyfile from the CI since it's a secret. + +## How to Deal with "TX Fee Exceeds the Configured Cap" Error +If the gas prices set for a transaction and the gas limit result in the transaction fee exceeding the maximum fee set for a given RPC node, you can try the following solutions: +1. **Try a Different RPC Node**: This setting is usually specific to the RPC node. Try using a different node, as it might have a higher limit. +2. **Decrease Gas Price**: If you are using legacy transactions and not using automated gas estimation, try decreasing the gas price in your TOML configuration. This will lower the overall transaction fee. +3. **Decrease Fee Cap**: If you are using EIP-1559 transactions and not using automated gas estimation, try decreasing the fee cap in your TOML configuration. You should also decrease the tip cap accordingly, as the fee cap includes both the base fee and the tip. This will lower the overall transaction fee. +4. **Decrease Gas Limit**: If you are using a hardcoded gas limit, try decreasing it. This will lower the overall transaction fee regardless of the transaction type. +5. **Use Gas Estimation**: If you are not using automated gas estimation, enable it. This will make Seth estimate gas for each transaction and adjust the gas price accordingly, which could prevent the error if your hardcoded gas-related values were too high. +6. **Use Different Gas Estimation Settings**: If you are using automated gas estimation, try lowering the gas estimation settings in your TOML configuration. Adjust the number of blocks used for estimation or the priority of the transaction. +7. **Disable Gas Estimations**: If you are using automated gas estimation, you can try disabling it. This will make Seth use the hardcoded gas-related values from your TOML configuration. This could prevent the error if you set the values low enough, but be aware it might lead to other issues, such as long waits for transaction inclusion in a block. + +## How to use Seth's synchronous API +Seth is designed with a synchronous API to enhance usability and predictability. This feature is implemented through the `seth.Decode()` function, which waits for each transaction to be mined before proceeding. Depending on the Seth configuration, the function will: + +- **Decode transactions only if they are reverted**: This is the default setting. +- **Always decode transactions**: This occurs if the `tracing_level` is set to `all`. +- **Always try to get revert reason**: if the transaction is reverted, Seth will try to get the revert reason, regardless of the `tracing_level` setting. + +This approach simplifies the way transactions are handled, making them more predictable and easier to debug. Therefore, it is highly recommended that you wrap all contract interactions in that method. + +## How to read Event Data from transactions +Retrieving event data from transactions in Seth involves a few steps but is not overly complicated. Below is a Go function example that illustrates how to capture event data from a specific transaction: + +```go +func (v *EthereumVRFCoordinatorV2_5) CancelSubscription(subID *big.Int, to common.Address) (*seth.DecodedTransaction, *vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCanceled, error) { + // execute a transaction + tx, err := v.client.Decode(v.coordinator.CancelSubscription(v.client.NewTXOpts(), subID, to)) + if err != nil { + return nil, nil, err + } + + // define the event you are looking for + var cancelEvent *vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCanceled + // iterate over receipt logs until you find a topic that matches the event you are looking for + for _, log := range tx.Receipt.Logs { + for _, topic := range log.Topics { + if topic.Cmp(vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCanceled{}.Topic()) == 0 { + // use geth wrapper to parse the log to the event struct + cancelEvent, err = v.coordinator.ParseSubscriptionCanceled(*log) + if err != nil { + return nil, nil, fmt.Errorf("parsing SubscriptionCanceled log failed, err: %w", err) + } + } + } + } + return tx, cancelEvent, err +} +``` + +This function demonstrates how to decode a transaction, check for specific event topics in the transaction logs, and parse those events. It's a structured approach to handling event data that is crucial for applications that need to respond to specific changes in state reflected by those events. + +## How to deal with Failed Transactions +When a state-changing interaction with a contract fails it's often difficult to know why it failed without using dedicated tools like Tenderly. That's why we have included transaction tracing and decoding in Seth. By default, it only applies to reverted transactions, +but you can enable it for all transactions by setting `tracing_level` to `all` in your TOML configuration. The information is printed to the console (with a mix of `debug` and `trace` levels), but you can also automatically save tracing information to a json file by setting `trace_to_json=true`. + +If you suspect your tests might run into failed transactions in hard to reproduce circumstances it's advised to: +* set `SETH_LOG_LEVEL` and `TEST_SETH_LOG_LEVEL` to `trace` +* for tests running in the CI/Docker enable `trace_to_json` in your TOML configuration and add upload `./traces` directory to the artifacts in your CI pipeline (it will be located in the same directory as `./logs`) +* make sure that in your code you upload contracts only using Seth's `ContractLoader` helper struct as it will automatically add contract's ABI to Seth's ABI cache -- and that's a prerequisite for decoding transactions (more info below) +* make sure that in your code when you load contracts manually you always add its ABI to Seth's ABI cache using `seth.ContractStore.AddABI()` function +* make sure that contracts you are using are not very heavily optimised (as the optimiser might remove custom revert reasons) and that your EVM target version is >= Constantinople hard fork (as custom revert reasons are not supported in earlier versions) + +Example of loading contract using `ContractLoader`: +```go +func LoadOffchainAggregator(client *seth.Client, contractAddress common.Address) (offchainaggregator.OffchainAggregator, error) { + // intialize contract loader with the generic type of the Geth contract wrapper + loader := seth.NewContractLoader[offchainaggregator.OffchainAggregator](client) + // call load function with contract name, address, ABI getter function and Geth wrapper constructor function + ocr, err := loader.LoadContract("OffChainAggregator", contractAddress, offchainaggregator.OffchainAggregatorMetaData.GetAbi, offchainaggregator.NewOffchainAggregator) + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + } +``` + +## How Automated Gas Estimation works +Gas estimation varies based on whether the network is a private Ethereum Network or a live network. + +- **Private Ethereum Networks**: no estimation is ever done. We always use hardcoded values. + +For real networks, the estimation process differs for legacy transactions and those compliant with EIP-1559: + +##### Legacy Transactions +1. **Initial Price**: Query the network node for the current suggested gas price. +2. **Priority Adjustment**: Modify the initial price based on `gas_price_estimation_tx_priority`. Higher priority increases the price to ensure faster inclusion in a block. +3. **Congestion Analysis**: Examine the last X blocks (as specified by `gas_price_estimation_blocks`) to determine network congestion, calculating the usage rate of gas in each block and giving recent blocks more weight. +4. **Buffering**: Add a buffer to the adjusted gas price to increase transaction reliability during high congestion. + +##### EIP-1559 Transactions +1. **Tip Fee Query**: Ask the node for the current recommended tip fee. +2. **Fee History Analysis**: Gather the base fee and tip history from recent blocks to establish a fee baseline. +3. **Fee Selection**: Use the greater of the node's suggested tip or the historical average tip for upcoming calculations. +4. **Priority and Adjustment**: Increase the base and tip fees based on transaction priority (`gas_price_estimation_tx_priority`), which influences how much you are willing to spend to expedite your transaction. +5. **Final Fee Calculation**: Sum the base fee and adjusted tip to set the `gas_fee_cap`. +6. **Congestion Buffer**: Similar to legacy transactions, analyze congestion and apply a buffer to both the fee cap and the tip to secure transaction inclusion. + +Understanding and setting these parameters correctly ensures that your transactions are processed efficiently and cost-effectively on the network. + +Finally, `gas_price_estimation_tx_priority` is also used, when deciding, which percentile to use for base fee and tip for historical fee data. Here's how that looks: +```go + case Priority_Fast: + baseFee = stats.GasPrice.Perc99 + historicalGasTipCap = stats.TipCap.Perc99 + case Priority_Standard: + baseFee = stats.GasPrice.Perc50 + historicalGasTipCap = stats.TipCap.Perc50 + case Priority_Slow: + baseFee = stats.GasPrice.Perc25 + historicalGasTipCap = stats.TipCap.Perc25 +``` + +##### Adjustment factor +All values are multiplied by the adjustment factor, which is calculated based on `gas_price_estimation_tx_priority`: +```go + case Priority_Fast: + return 1.2 + case Priority_Standard: + return 1.0 + case Priority_Slow: + return 0.8 +``` +For fast transactions we will increase gas price by 20%, for standard we will use the value as is and for slow we will decrease it by 20%. + +##### Buffer percents +We further adjust the gas price by adding a buffer to it, based on congestion rate: +```go + case Congestion_Low: + return 1.10, nil + case Congestion_Medium: + return 1.20, nil + case Congestion_High: + return 1.30, nil + case Congestion_VeryHigh: + return 1.40, nil +``` + +For low congestion rate we will increase gas price by 10%, for medium by 20%, for high by 30% and for very high by 40%. +We cache block header data in an in-memory cache, so we don't have to fetch it every time we estimate gas. The cache has capacity equal to `gas_price_estimation_blocks` and every time we add a new element, we remove one that is least frequently used and oldest (with block number being a constant and chain always moving forward it makes no sense to keep old blocks). + +It's important to know that in order to use congestion metrics we need to fetch at least 80% of the requested blocks. If that fails, we will skip this part of the estimation and only adjust the gas price based on priority. + +For both transaction types if any of the steps fails, we fallback to hardcoded values. + +## How to tweak Automated Gas Estimation +Now that you know how automated gas estimation works, you can tweak it to better suit your needs. Here are some tips to help you optimize your gas estimation process: +* **Adjust the Gas Price Estimation Blocks**: Increase or decrease the number of blocks used for gas estimation based on network conditions. More blocks provide a more accurate picture of network congestion and can smooth out short spikes of high gas prices. On the other hand, fewer blocks can speed up the estimation process or even be a prerequisite for the estimation to work (if the RPC is slow). Remember also that longer block range can lead to less accurate estimation, if network conditions changed very recently (although the algorithm tries to counter that by assigning higher weights to more recent blocks. +* **Set the Gas Price Estimation Tx Priority**: Choose the transaction priority that best suits your needs. A higher priority will increase the gas price, ensuring faster inclusion in a block. Conversely, a lower priority will reduce the gas price, potentially saving you money. +* **Disable Gas Estimation**: If you prefer to use hardcoded values, you can disable automated gas estimation. This will make Seth use the hardcoded values from your TOML configuration, which can be useful if you want to avoid the overhead of estimation or if you have specific gas prices you want to use or in a rare case that estimations are inaccurate. + +## How to debug with 'execution reverted' error +When you encounter the 'execution reverted' error without any additional details, it can be challenging to determine the cause. This error indicates that the Ethereum Virtual Machine (EVM) couldn't return a specific revert reason. Here are some tips to help you debug this issue: +1. **Set SETH_LOG_LEVEL**: Increase the log level to `trace` to get more detailed information about the transaction. This will help you identify the cause of the revert. +2. **Use Custom Revert Reasons**: Ensure your contract uses custom error/custom messages with `revert`/`require` statements. Without them, you won't get specific reasons for the error. +3. **Check Solidity Version**: Make sure your contract uses a Solidity version that supports custom revert reasons (>= 0.8.4). The version is specified at the beginning of your contract file, like this: + ```solidity + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.4; + ``` + If the version is lower, you won't get custom revert reasons. Upgrading the version might introduce breaking changes, so do not commit these changes without thorough testing. +4. **Inspect `delegatecall` Usage**: If the method you are calling uses `delegatecall`, it might be the cause of the issue. `delegatecall` is a low-level call that doesn't return revert reasons, so you'll always get the 'execution reverted' error unless you manually handle it in assembly. +5. **Debug with Events**: If you're still unable to find the cause, you can modify the contract to emit an event with the revert reason. For example, define an event like `event DebugEvent(string reason)` and add it before the points where you suspect a revert might occur. You will be able to find events in the transaction receipt's logs. Remember to remove these changes after debugging. +6. **Check EVM Node Logs**: If all else fails, the issue might be that Seth cannot decode the revert reason. Look at the logs of the EVM node for 'execution reverted' errors and any additional information (e.g., `errdata=0x46f08154`). If you find more details there but not in your test output, it might be a bug in Seth, and you should contact the Test Tooling team. + +By following these steps, you can systematically identify and address the cause of 'execution reverted' errors in your smart contracts. + +## How to split non-native tokens between keys +Currently, Seth doesn't support splitting non-native tokens between keys. If you need to split non-native tokens between keys, you can use the following approach: +1. **Fund root key** with the total amount of tokens you want to distribute. +2. **Deploy v contract** that allows to execute multiple operations in a single transaction. +3. **Prepare payload for Multicall contract** that will call `transfer` function on the token contract for each key. +4. **Execute Multicall contract** with the payload prepared in the previous step. + +You can find sample code for doing that for LINK token in [actions_seth](./actions/seth/actions.go) file as `SendLinkFundsToDeploymentAddresses()` method. \ No newline at end of file diff --git a/integration-tests/actions/seth/actions.go b/integration-tests/actions/seth/actions.go index 91f03c36f1f..68f6df9b49e 100644 --- a/integration-tests/actions/seth/actions.go +++ b/integration-tests/actions/seth/actions.go @@ -33,11 +33,11 @@ import ( ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ContractDeploymentInterval = 200 @@ -924,7 +924,7 @@ var noOpSethConfigFn = func(cfg *seth.Config) error { return nil } type SethConfigFunction = func(*seth.Config) error // OneEphemeralKeysLiveTestnetCheckFn checks whether there's at least one ephemeral key on a simulated network or at least one static key on a live network, -// and that there are no epehemeral keys on a live network. Root key is excluded from the check. +// and that there are no ephemeral keys on a live network. Root key is excluded from the check. var OneEphemeralKeysLiveTestnetCheckFn = func(sethCfg *seth.Config) error { concurrency := sethCfg.GetMaxConcurrency() @@ -994,7 +994,7 @@ func GetChainClientWithConfigFunction(config ctf_config.SethConfig, network bloc return nil, fmt.Errorf("Seth config not found") } - sethCfg, err := utils.MergeSethAndEvmNetworkConfigs(network, *readSethCfg) + sethCfg, err := seth_utils.MergeSethAndEvmNetworkConfigs(network, *readSethCfg) if err != nil { return nil, errors.Wrapf(err, "Error merging seth and evm network configs") } @@ -1004,7 +1004,7 @@ func GetChainClientWithConfigFunction(config ctf_config.SethConfig, network bloc return nil, errors.Wrapf(err, "Error applying seth config function") } - err = utils.ValidateSethNetworkConfig(sethCfg.Network) + err = seth_utils.ValidateSethNetworkConfig(sethCfg.Network) if err != nil { return nil, errors.Wrapf(err, "Error validating seth network config") } diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index a3d50ae9874..b06c2564a88 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -25,7 +26,6 @@ import ( tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" "github.com/smartcontractkit/chainlink/integration-tests/types" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -143,7 +143,7 @@ func TestAutomationBenchmark(t *testing.T) { benchmarkTestNetwork := getNetworkConfig(&config) l.Info().Str("Namespace", testEnvironment.Cfg.Namespace).Msg("Connected to Keepers Benchmark Environment") - testNetwork := utils.MustReplaceSimulatedNetworkUrlWithK8(l, benchmarkNetwork, *testEnvironment) + testNetwork := seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, benchmarkNetwork, *testEnvironment) chainClient, err := actions_seth.GetChainClientWithConfigFunction(&config, testNetwork, actions_seth.OneEphemeralKeysLiveTestnetAutoFixFn) require.NoError(t, err, "Error getting Seth client") diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 7989150fbf0..fcbf8f5a471 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -6,6 +6,8 @@ import ( "testing" "time" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" + "github.com/onsi/gomega" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" @@ -25,8 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - "github.com/smartcontractkit/chainlink/integration-tests/utils" - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -243,7 +243,7 @@ func TestAutomationChaos(t *testing.T) { chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) require.NoError(t, err, "Error connecting to Chainlink nodes") - network = utils.MustReplaceSimulatedNetworkUrlWithK8(l, network, *testEnvironment) + network = seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, network, *testEnvironment) chainClient, err := actions_seth.GetChainClientWithConfigFunction(&config, network, actions_seth.OneEphemeralKeysLiveTestnetAutoFixFn) require.NoError(t, err, "Error creating seth client") diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index 9ff697748dc..dfd1efaefe0 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -28,7 +29,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCR2VRFChaos(t *testing.T) { @@ -156,7 +156,7 @@ func TestOCR2VRFChaos(t *testing.T) { err = testEnvironment.Client.LabelChaosGroup(testEnvironment.Cfg.Namespace, "instance=node-", 3, 5, ChaosGroupMajority) require.NoError(t, err) - testNetwork = utils.MustReplaceSimulatedNetworkUrlWithK8(l, testNetwork, *testEnvironment) + testNetwork = seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, testNetwork, *testEnvironment) chainClient, err := actions_seth.GetChainClientWithConfigFunction(testconfig, testNetwork, actions_seth.OneEphemeralKeysLiveTestnetCheckFn) require.NoError(t, err, "Error creating seth client") diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index da1356689ad..9f7dea3c258 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -20,13 +20,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -166,7 +166,7 @@ func TestOCRChaos(t *testing.T) { cfg := config.MustCopy().(tc.TestConfig) network := networks.MustGetSelectedNetworkConfig(cfg.GetNetworkConfig())[0] - network = utils.MustReplaceSimulatedNetworkUrlWithK8(l, network, *testEnvironment) + network = seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, network, *testEnvironment) seth, err := actions_seth.GetChainClient(&cfg, network) require.NoError(t, err, "Error creating seth client") diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 3733a827ed1..3a4c0ccfc80 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -28,11 +28,11 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 - github.com/smartcontractkit/chainlink-testing-framework v1.28.15 + github.com/smartcontractkit/chainlink-testing-framework v1.28.17 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c - github.com/smartcontractkit/seth v1.0.9 + github.com/smartcontractkit/seth v1.0.11 github.com/smartcontractkit/wasp v0.4.7 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 06383fe3a59..f9a7e8a3f12 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1524,8 +1524,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b2 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= -github.com/smartcontractkit/chainlink-testing-framework v1.28.15 h1:mga7N6jtXQ3UOCt43IdsEnCMBh9xjOWPaE9BiM6kr6Q= -github.com/smartcontractkit/chainlink-testing-framework v1.28.15/go.mod h1:x1zDOz8zcLjEvs9fNA9y/DMguLam/2+CJdpxX0+rM8A= +github.com/smartcontractkit/chainlink-testing-framework v1.28.17 h1:zezoeiG3GUGW1T2+genS/HD1BvRJwC3rqFnFTFNB9aY= +github.com/smartcontractkit/chainlink-testing-framework v1.28.17/go.mod h1:xjxJK+4SUjBmJJWfRFl02poauU4XQE37aH7WYtxTLKg= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 h1:fX/xmGm1GBsD1ZZnooNT+eWA0hiTAqFlHzOC5CY4dy8= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449/go.mod h1:DC8sQMyTlI/44UCTL8QWFwb0bYNoXCfjwCv2hMivYZU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= @@ -1536,8 +1536,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c h1:lIyMbTaF2H0Q71vkwZHX/Ew4KF2BxiKhqEXwF8rn+KI= github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= -github.com/smartcontractkit/seth v1.0.9 h1:v+gxRY5JT9u4Ptk1mg/Sm76aqdG2vFw1zq1Ngwoj6yk= -github.com/smartcontractkit/seth v1.0.9/go.mod h1:2TMOZQ8WTAw7rR1YBbXpnad6VmT/+xDd/nXLmB7Eero= +github.com/smartcontractkit/seth v1.0.11 h1:Ct8wSifW2ZXnyHtYRKaawBnpW7Ef0m8zItUcqYPallM= +github.com/smartcontractkit/seth v1.0.11/go.mod h1:fVCE+8LD6AbU7d8BHZ1uS/ceJ+8JO0iVTUcDdQmGPAc= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index 7399c08c0d4..7f066aa8bcc 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -34,6 +34,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/wiremock" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" ctfconfig "github.com/smartcontractkit/chainlink-testing-framework/config" @@ -47,7 +48,6 @@ import ( tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" aconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/automation" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_compatible_utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" @@ -318,7 +318,7 @@ Load Config: err = testEnvironment.Run() require.NoError(t, err, "Error running chainlink DON") - testNetwork = utils.MustReplaceSimulatedNetworkUrlWithK8(l, testNetwork, *testEnvironment) + testNetwork = seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, testNetwork, *testEnvironment) chainClient, err := actions_seth.GetChainClientWithConfigFunction(loadedTestConfig, testNetwork, actions_seth.OneEphemeralKeysLiveTestnetCheckFn) require.NoError(t, err, "Error creating seth client") diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index b50577120af..d81845f4b3c 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -17,11 +17,11 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524173852-a74b009c7303 - github.com/smartcontractkit/chainlink-testing-framework v1.28.15 + github.com/smartcontractkit/chainlink-testing-framework v1.28.17 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c - github.com/smartcontractkit/seth v1.0.9 + github.com/smartcontractkit/seth v1.0.11 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wasp v0.4.7 github.com/stretchr/testify v1.9.0 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index c50f8f40fc5..edcb5c3a5b2 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1514,8 +1514,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b2 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= -github.com/smartcontractkit/chainlink-testing-framework v1.28.15 h1:mga7N6jtXQ3UOCt43IdsEnCMBh9xjOWPaE9BiM6kr6Q= -github.com/smartcontractkit/chainlink-testing-framework v1.28.15/go.mod h1:x1zDOz8zcLjEvs9fNA9y/DMguLam/2+CJdpxX0+rM8A= +github.com/smartcontractkit/chainlink-testing-framework v1.28.17 h1:zezoeiG3GUGW1T2+genS/HD1BvRJwC3rqFnFTFNB9aY= +github.com/smartcontractkit/chainlink-testing-framework v1.28.17/go.mod h1:xjxJK+4SUjBmJJWfRFl02poauU4XQE37aH7WYtxTLKg= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 h1:fX/xmGm1GBsD1ZZnooNT+eWA0hiTAqFlHzOC5CY4dy8= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449/go.mod h1:DC8sQMyTlI/44UCTL8QWFwb0bYNoXCfjwCv2hMivYZU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= @@ -1526,8 +1526,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c h1:lIyMbTaF2H0Q71vkwZHX/Ew4KF2BxiKhqEXwF8rn+KI= github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= -github.com/smartcontractkit/seth v1.0.9 h1:v+gxRY5JT9u4Ptk1mg/Sm76aqdG2vFw1zq1Ngwoj6yk= -github.com/smartcontractkit/seth v1.0.9/go.mod h1:2TMOZQ8WTAw7rR1YBbXpnad6VmT/+xDd/nXLmB7Eero= +github.com/smartcontractkit/seth v1.0.11 h1:Ct8wSifW2ZXnyHtYRKaawBnpW7Ef0m8zItUcqYPallM= +github.com/smartcontractkit/seth v1.0.11/go.mod h1:fVCE+8LD6AbU7d8BHZ1uS/ceJ+8JO0iVTUcDdQmGPAc= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index a962dbfd6bb..bf7449a2470 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -7,10 +7,11 @@ import ( "github.com/smartcontractkit/seth" "github.com/smartcontractkit/wasp" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" vrfv2_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) type BHSTestGun struct { @@ -53,7 +54,7 @@ func (m *BHSTestGun) Call(_ *wasp.Generator) *wasp.Response { *m.testConfig.General.NumberOfWords, *m.testConfig.General.RandomnessRequestCountPerRequest, *m.testConfig.General.RandomnessRequestCountPerRequestDeviation, - utils.AvailableSethKeyNum(m.sethClient), + seth_utils.AvailableSethKeyNum(m.sethClient), ) //todo - might need to store randRequestBlockNumber and blockhash to verify that it was stored in BHS contract at the end of the test if err != nil { @@ -110,7 +111,7 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { randomnessRequestCountPerRequest, *vrfv2Config.RandomnessRequestCountPerRequestDeviation, vrfv2Config.RandomWordsFulfilledEventTimeout.Duration, - utils.AvailableSethKeyNum(m.sethClient), + seth_utils.AvailableSethKeyNum(m.sethClient), ) if err != nil { return &wasp.Response{Error: err.Error(), Failed: true} diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index c95e7161322..79f477294bb 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -9,10 +9,11 @@ import ( "github.com/smartcontractkit/seth" "github.com/smartcontractkit/wasp" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2plus" vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) type BHSTestGun struct { @@ -59,7 +60,7 @@ func (m *BHSTestGun) Call(_ *wasp.Generator) *wasp.Response { billingType, vrfv2PlusConfig, m.logger, - utils.AvailableSethKeyNum(m.sethClient), + seth_utils.AvailableSethKeyNum(m.sethClient), ) //todo - might need to store randRequestBlockNumber and blockhash to verify that it was stored in BHS contract at the end of the test if err != nil { @@ -118,7 +119,7 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { billingType, vrfv2PlusConfig, m.logger, - utils.AvailableSethKeyNum(m.sethClient), + seth_utils.AvailableSethKeyNum(m.sethClient), ) if err != nil { return &wasp.Response{Error: err.Error(), Failed: true} diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index 83b96828444..fe6515df03f 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -16,6 +16,7 @@ import ( eth "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -26,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ocr2vrfSmokeConfig *testconfig.TestConfig @@ -46,7 +46,7 @@ func TestOCR2VRFRedeemModel(t *testing.T) { return } - testNetwork = utils.MustReplaceSimulatedNetworkUrlWithK8(l, testNetwork, *testEnvironment) + testNetwork = seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, testNetwork, *testEnvironment) chainClient, err := actions_seth.GetChainClientWithConfigFunction(config, testNetwork, actions_seth.OneEphemeralKeysLiveTestnetCheckFn) require.NoError(t, err, "Error creating seth client") @@ -109,7 +109,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { return } - testNetwork = utils.MustReplaceSimulatedNetworkUrlWithK8(l, testNetwork, *testEnvironment) + testNetwork = seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, testNetwork, *testEnvironment) chainClient, err := actions_seth.GetChainClientWithConfigFunction(config, testNetwork, actions_seth.OneEphemeralKeysLiveTestnetCheckFn) require.NoError(t, err, "Error creating seth client") diff --git a/integration-tests/testconfig/README.md b/integration-tests/testconfig/README.md index a4e165ba245..bb3d7c000f7 100644 --- a/integration-tests/testconfig/README.md +++ b/integration-tests/testconfig/README.md @@ -17,12 +17,46 @@ The `testconfig` package serves as a centralized resource for accessing configur The order of precedence for overrides is as follows: -* Environment variable `BASE64_CONFIG_OVERRIDE` -* File `overrides.toml` -* Product-specific file, e.g., `[product_name].toml` -* The `default.toml` file +* [File `default.toml`](#defaulttoml) +* [Product-specific file, e.g., `[product_name].toml`](#product-specific-configurations) +* [File `overrides.toml`](#overridestoml) +* [Environment variable `BASE64_CONFIG_OVERRIDE`](#base64_config_override) -The `BASE64_CONFIG_OVERRIDE` environment variable is primarily intended for use in continuous integration environments, enabling the substitution of default settings with confidential or user-specific parameters. For instance: +### default.toml +That file is envisioned to contain fundamental and universally applicable settings, such as logging configurations, private Ethereum network settings or Seth networks settings for known networks. + +### Product-specific configurations +Product-specific configurations, such as those in `[product_name].toml`, house the bulk of default and variant settings, supporting default configurations like the following in `log_poller.toml`, which should be used by all Log Poller tests: + +```toml +# product defaults +[LogPoller] +[LogPoller.General] +generator = "looped" +contracts = 2 +events_per_tx = 4 +use_finality_tag = true +log_poll_interval = "500ms" +# 0 disables backup poller +backup_log_poller_block_delay = 0 + +[LogPoller.Looped] +execution_count = 100 +min_emit_wait_time_ms = 200 +max_emit_wait_time_ms = 500 +``` + +### overrides.toml +This file is recommended for local use to adjust dynamic variables or modify predefined settings. At the very minimum it should contain the Chainlink image and version, as shown in the example below: + +```toml +[ChainlinkImage] +image = "your image name" +version = "your tag" +``` + +### `BASE64_CONFIG_OVERRIDE` +This environment variable is primarily intended for use in continuous integration environments, enabling the substitution of default settings with confidential or user-specific parameters. For instance: ```bash cat << EOF > config.toml @@ -61,38 +95,11 @@ BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) echo ::add-mask::$BASE64_CONFIG_OVERRIDE echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV ``` - **It is highly recommended to use reusable GHA actions present in [.actions](../../../.github/.actions) to generate and apply the base64-encoded configuration.** Own implementation of `BASE64_CONFIG_OVERRIDE` generation is discouraged and should be used only if existing actions do not cover the use case. But even in that case it might be a better idea to extend existing actions. - This variable is automatically relayed to Kubernetes-based tests, eliminating the need for manual intervention in test scripts. -The `overrides.toml` file is recommended for local use to adjust dynamic variables or modify predefined settings. At the very minimum it should contain the Chainlink image and version, as shown in the example below: - -```toml -[ChainlinkImage] -image = "your image name" -version = "your tag" -``` -Product-specific configurations, such as those in `[product_name].toml`, house the bulk of default and variant settings, supporting default configurations like the following in `log_poller.toml`: - -```toml -# product defaults -[LogPoller] -[LogPoller.General] -generator = "looped" -contracts = 2 -events_per_tx = 4 -use_finality_tag = true -log_poll_interval = "500ms" -# 0 disables backup poller -backup_log_poller_block_delay = 0 - -[LogPoller.Looped] -execution_count = 100 -min_emit_wait_time_ms = 200 -max_emit_wait_time_ms = 500 -``` +## Named Configurations Named configurations allow for the customization of settings through unique identifiers, such as a test name or type, acting as specific overrides. Here's how you can define and use these configurations: @@ -114,8 +121,6 @@ cancel_subs_after_test_run = true When processing TOML files, the system initially searches for a general (unnamed) configuration. If a named configuration is found, it can specifically override the general (unnamed) settings, providing a targeted approach to configuration management based on distinct identifiers like test names or types. -Finally `default.toml` file is envisioned to contain fundamental and universally applicable settings, such as logging configurations. - ### Chainlink Node TOML config Find default node config in `testconfig/default.toml` @@ -211,7 +216,7 @@ Essential variables might include: For local testing, it is advisable to place these variables in the `overrides.toml` file. For Kubernetes or remote runners, the process involves creating a TOML file with the necessary values, encoding it in base64, and setting the result as the `BASE64_CONFIG_OVERRIDE` environment variable. -## Embeded config +## Embedded config Because Go automatically excludes TOML files during the compilation of binaries, we must take deliberate steps to include our configuration files in the compiled binary. This can be accomplished by using a custom build tag `-o embed`. Implementing this tag will incorporate all the default configurations located in the `./testconfig` folder directly into the binary. Therefore, when executing tests from the binary, you'll only need to supply the `overrides.toml` file. This file should list only the settings you wish to modify; all other configurations will be sourced from the embedded configurations. You can access these embedded configurations [here](.integration-tests/testconfig/configs_embed.go). diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index f937faf9c24..451f620fe3f 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -97,6 +97,7 @@ tracing_level = "reverted" # were able te decode, we try to save maximum information possible. It can either be: # just tx hash, decoded transaction or call trace. Which transactions traces are saved depends # on 'tracing_level'. + # number of addresses to be generated and runtime, if set to 0, no addresses will be generated # each generated address will receive a proportion of native tokens from root private key's balance # with the value equal to (root_balance / ephemeral_addresses_number) - transfer_fee * ephemeral_addresses_number @@ -106,21 +107,49 @@ ephemeral_addresses_number = 10 # That's because the one we are about to send would get queued, possibly for a very long time pending_nonce_protection_enabled = true -root_key_funds_buffer = 1000 +# Amount to be left on root key/address, when we are using ephemeral addresses. It's the amount that will not +# be divided into ephemeral keys. Default value is good for simulated networks, but you should change it for +# something much more reasonable for live networks. +root_key_funds_buffer = 1000 # 1000 ethers # when enabled when creating a new Seth client we will send 10k wei from root address to root address # to make sure transaction can be submited and mined check_rpc_health_on_start = false +# feature-flagged expriments: "slow_funds_return" sets funds return priority to 'slow' (core only!); +# "eip_1559_fee_equalizer" sets the tip/base fee to the higher value in case there's 3+ orders of magnitude difference between them +experiments_enabled = [] + [Seth.nonce_manager] +# rate-limiting of key syncs, to prevent spamming the node with nonce calls key_sync_rate_limit_per_sec = 10 +# how long to wait for a key sync to complete before giving up key_sync_timeout = "100s" +# how long to wait before retrying a key sync key_sync_retry_delay = "1s" +# how many times to retry a key sync before giving up key_sync_retries = 10 +# this is a default config that will be used if you haven't specified any network specific settings +# you can always override that by providing network specific settings like in the examples below +[[Seth.networks]] +name = "Default" +transaction_timeout = "60s" +# enable EIP-1559 transactions, because Seth will disable them if they are not supported +eip_1559_dynamic_fees = true +# enable automated gas estimation, because Seth will auto-disable it if any of the required JSON RPC methods are missing +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 100 +gas_price_estimation_tx_priority = "standard" + +# fallback values +transfer_gas_fee = 21_000 +gas_price = 150_000_000_000 #150 gwei +gas_fee_cap = 150_000_000_000 #150 gwei +gas_tip_cap = 50_000_000_000 #50 gwei + [[Seth.networks]] name = "Geth" -chain_id = "1337" transaction_timeout = "30s" # gas limits @@ -138,8 +167,7 @@ gas_fee_cap = 15_000_000_000 gas_tip_cap = 5_000_000_000 [[Seth.networks]] -name = "AVALANCHE_FUJI" -chain_id = "43113" +name = "Avalanche Fuji" transaction_timeout = "3m" eip_1559_dynamic_fees = true @@ -159,7 +187,7 @@ eip_1559_dynamic_fees = true # gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions) # gas_limit = 8_000_000 # transfer_gas_fee is gas limit that will be used, when funding CL nodes and returning funds from there and when funding and returning funds from ephemeral keys -# we use hardcoded value in order to be estimate how much funds are available for sending or returning after tx costs have been paid +# we use hardcoded value in order to estimate how much funds are available for sending or returning after tx costs have been paid transfer_gas_fee = 21_000 # manual settings, used when gas_price_estimation_enabled is false or when it fails @@ -171,8 +199,7 @@ gas_fee_cap = 30_000_000_000 gas_tip_cap = 1_800_000_000 [[Seth.networks]] -name = "Sepolia" -chain_id = "11155111" +name = "Sepolia Testnet" transaction_timeout = "3m" eip_1559_dynamic_fees = false @@ -204,8 +231,7 @@ gas_fee_cap = 45_000_000_000 gas_tip_cap = 10_000_000_000 [[Seth.networks]] -name = "POLYGON_MUMBAI" -chain_id = "80001" +name = "Polygon Mumbai" transaction_timeout = "3m" eip_1559_dynamic_fees = true @@ -237,8 +263,7 @@ gas_fee_cap = 3_800_000_000 gas_tip_cap = 1_800_000_000 [[Seth.networks]] -name = "POLYGON_AMOY" -chain_id = "80002" +name = "Polygon Amoy" transaction_timeout = "3m" eip_1559_dynamic_fees = true @@ -263,15 +288,14 @@ transfer_gas_fee = 21_000 # manual settings, used when gas_price_estimation_enabled is false or when it fails # legacy transactions -gas_price = 1_800_000_000 +gas_price = 200_000_000_000 # EIP-1559 transactions -gas_fee_cap = 3_800_000_000 -gas_tip_cap = 1_800_000_000 +gas_fee_cap = 200_000_000_000 +gas_tip_cap = 2_000_000_000 [[Seth.networks]] -name = "zkEVM" -chain_id = "1442" +name = "Polygon zkEVM Goerli" transaction_timeout = "3m" eip_1559_dynamic_fees = false @@ -302,64 +326,8 @@ gas_price = 50_000_000 gas_fee_cap = 3_800_000_000 gas_tip_cap = 1_800_000_000 - -[[Seth.networks]] -name = "ARBITRUM_SEPOLIA" -chain_id = "421614" -transaction_timeout = "1m" - -# if set to true we will estimate gas for every transaction -gas_price_estimation_enabled = true - -# transfer_gas_fee is gas limit that will be used, when funding CL nodes and returning funds from there and when funding and returning funds from ephemeral keys -# we use hardcoded value in order to be estimate how much funds are available for sending or returning after tx costs have been paid -transfer_gas_fee = 50_000 -# gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions) -# gas_limit = 100_000_000 - -# manual settings, used when gas_price_estimation_enabled is false or when it fails -# legacy transactions -gas_price = 50_000_000_000 -# EIP-1559 transactions -eip_1559_dynamic_fees = true -gas_fee_cap = 50_000_000_000 -gas_tip_cap = 2_000_000_000 - -# how many last blocks to use, when estimating gas for a transaction -gas_price_estimation_blocks = 100 -# priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor will be used for gas estimation) [default: "standard"] -gas_price_estimation_tx_priority = "standard" - [[Seth.networks]] -name = "POLYGON_AMOY" -chain_id = "80002" -transaction_timeout = "1m" - -# if set to true we will estimate gas for every transaction -gas_price_estimation_enabled = true - -# transfer_gas_fee is gas limit that will be used, when funding CL nodes and returning funds from there and when funding and returning funds from ephemeral keys -# we use hardcoded value in order to be estimate how much funds are available for sending or returning after tx costs have been paid -transfer_gas_fee = 21_000 -# gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions) -# gas_limit = 100_000_000 - -# manual settings, used when gas_price_estimation_enabled is false or when it fails -# legacy transactions -gas_price = 200_000_000_000 -# EIP-1559 transactions -eip_1559_dynamic_fees = true -gas_fee_cap = 200_000_000_000 -gas_tip_cap = 2_000_000_000 - -# how many last blocks to use, when estimating gas for a transaction -gas_price_estimation_blocks = 100 -# priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor will be used for gas estimation) [default: "standard"] -gas_price_estimation_tx_priority = "standard" - -[[Seth.networks]] -name = "OPTIMISM_SEPOLIA" -chain_id = "11155420" +name = "Optimism Sepolia" transaction_timeout = "3m" # if set to true we will estimate gas for every transaction @@ -386,8 +354,7 @@ gas_price_estimation_tx_priority = "standard" [[Seth.networks]] -name = "BASE_SEPOLIA" -chain_id = "84532" +name = "Base Sepolia" transaction_timeout = "3m" # if set to true we will estimate gas for every transaction @@ -412,3 +379,20 @@ gas_price_estimation_blocks = 50 # priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor will be used for gas estimation) [default: "standard"] gas_price_estimation_tx_priority = "standard" +[[Seth.networks]] +name = "Arbitrum Sepolia" +transaction_timeout = "10m" +transfer_gas_fee = 50_000 +# gas_limit = 15_000_000 +# legacy transactions +gas_price = 200_000_000_000 +# EIP-1559 transactions +eip_1559_dynamic_fees = true +gas_fee_cap = 200_000_000_000 +gas_tip_cap = 10_000_000_000 +# if set to true we will estimate gas for every transaction +gas_price_estimation_enabled = true +# how many last blocks to use, when estimating gas for a transaction +gas_price_estimation_blocks = 100 +# priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor will be used for gas estimation) [default: "standard"] +gas_price_estimation_tx_priority = "standard" \ No newline at end of file diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 337c960204a..ff2ddf3a449 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -384,8 +384,9 @@ func GetConfig(configurationName string, product Product) (TestConfig, error) { if testConfig.Seth != nil && (testConfig.Seth.EphemeralAddrs != nil && *testConfig.Seth.EphemeralAddrs != 0) { rootBuffer := testConfig.Seth.RootKeyFundsBuffer + zero := int64(0) if rootBuffer == nil { - rootBuffer = big.NewInt(0) + rootBuffer = &zero } clNodeFunding := testConfig.Common.ChainlinkNodeFunding if clNodeFunding == nil { @@ -398,9 +399,7 @@ func GetConfig(configurationName string, product Product) (TestConfig, error) { minRequiredFundsBuffered := big.NewFloat(0).Mul(minRequiredFunds, big.NewFloat(1.2)) minRequiredFundsBufferedInt, _ := minRequiredFundsBuffered.Int(nil) - rootBuffer64, _ := rootBuffer.Float64() - - if big.NewFloat(rootBuffer64).Cmp(minRequiredFundsBuffered) <= 0 { + if *rootBuffer < minRequiredFundsBufferedInt.Int64() { msg := ` The funds allocated to the root key buffer are below the minimum requirement, which could lead to insufficient funds for performing contract deployments. Please review and adjust your TOML configuration file to ensure that the root key buffer has adequate funds. Increase the fund settings as necessary to meet this requirement. @@ -410,7 +409,7 @@ root_key_funds_buffer = 1_000 ` logger.Warn(). - Str("Root key buffer (wei/ether)", fmt.Sprintf("%s/%s", rootBuffer.String(), conversions.WeiToEther(rootBuffer).Text('f', -1))). + Str("Root key buffer (wei/ether)", fmt.Sprintf("%s/%s", fmt.Sprint(rootBuffer), conversions.WeiToEther(big.NewInt(*rootBuffer)).Text('f', -1))). Str("Minimum required funds (wei/ether)", fmt.Sprintf("%s/%s", minRequiredFundsBuffered.String(), conversions.WeiToEther(minRequiredFundsBufferedInt).Text('f', -1))). Msg(msg) } diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index aaff28fcb10..0a08b08b586 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -37,6 +37,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -44,11 +45,9 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" tt "github.com/smartcontractkit/chainlink/integration-tests/types" - "github.com/smartcontractkit/chainlink/integration-tests/utils" - - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) const ( @@ -167,7 +166,7 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { network = networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] ) - network = utils.MustReplaceSimulatedNetworkUrlWithK8(o.log, network, *o.testEnvironment) + network = seth_utils.MustReplaceSimulatedNetworkUrlWithK8(o.log, network, *o.testEnvironment) seth, err := actions_seth.GetChainClient(o.Config, network) require.NoError(o.t, err, "Error creating seth client") diff --git a/integration-tests/utils/seth.go b/integration-tests/utils/seth.go deleted file mode 100644 index af953f49d48..00000000000 --- a/integration-tests/utils/seth.go +++ /dev/null @@ -1,146 +0,0 @@ -package utils - -import ( - "fmt" - "strconv" - - "github.com/rs/zerolog" - "github.com/smartcontractkit/seth" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" -) - -// MergeSethAndEvmNetworkConfigs merges EVMNetwork to Seth config. If Seth config already has Network settings, -// it will return unchanged Seth config that was passed to it. If the network is simulated, it will -// use Geth-specific settings. Otherwise it will use the chain ID to find the correct network settings. -// If no match is found it will return error. -func MergeSethAndEvmNetworkConfigs(evmNetwork blockchain.EVMNetwork, sethConfig seth.Config) (seth.Config, error) { - if sethConfig.Network != nil { - return sethConfig, nil - } - - var sethNetwork *seth.Network - - for _, conf := range sethConfig.Networks { - if evmNetwork.Simulated { - if conf.Name == seth.GETH { - conf.PrivateKeys = evmNetwork.PrivateKeys - if len(conf.URLs) == 0 { - conf.URLs = evmNetwork.URLs - } - // important since Besu doesn't support EIP-1559, but other EVM clients do - conf.EIP1559DynamicFees = evmNetwork.SupportsEIP1559 - - // might be needed for cases, when node is incapable of estimating gas limit (e.g. Geth < v1.10.0) - if evmNetwork.DefaultGasLimit != 0 { - conf.GasLimit = evmNetwork.DefaultGasLimit - } - - sethNetwork = conf - break - } - } else if conf.ChainID == fmt.Sprint(evmNetwork.ChainID) { - conf.PrivateKeys = evmNetwork.PrivateKeys - if len(conf.URLs) == 0 { - conf.URLs = evmNetwork.URLs - } - - sethNetwork = conf - break - } - } - - if sethNetwork == nil { - return seth.Config{}, fmt.Errorf("No matching EVM network found for chain ID %d. If it's a new network please define it as [Network.EVMNetworks.NETWORK_NAME] in TOML", evmNetwork.ChainID) - } - - sethConfig.Network = sethNetwork - - return sethConfig, nil -} - -// MustReplaceSimulatedNetworkUrlWithK8 replaces the simulated network URL with the K8 URL and returns the network. -// If the network is not simulated, it will return the network unchanged. -func MustReplaceSimulatedNetworkUrlWithK8(l zerolog.Logger, network blockchain.EVMNetwork, testEnvironment environment.Environment) blockchain.EVMNetwork { - if !network.Simulated { - return network - } - - networkKeys := []string{"Simulated Geth", "Simulated-Geth"} - var keyToUse string - - for _, key := range networkKeys { - _, ok := testEnvironment.URLs[key] - if ok { - keyToUse = key - break - } - } - - if keyToUse == "" { - for k := range testEnvironment.URLs { - l.Info().Str("Network", k).Msg("Available networks") - } - panic("no network settings for Simulated Geth") - } - - network.URLs = testEnvironment.URLs[keyToUse] - - return network -} - -// ValidateSethNetworkConfig validates the Seth network config -func ValidateSethNetworkConfig(cfg *seth.Network) error { - if cfg == nil { - return fmt.Errorf("Network cannot be nil") - } - if cfg.ChainID == "" { - return fmt.Errorf("ChainID is required") - } - _, err := strconv.Atoi(cfg.ChainID) - if err != nil { - return fmt.Errorf("ChainID needs to be a number") - } - if cfg.URLs == nil || len(cfg.URLs) == 0 { - return fmt.Errorf("URLs are required") - } - if cfg.PrivateKeys == nil || len(cfg.PrivateKeys) == 0 { - return fmt.Errorf("PrivateKeys are required") - } - if cfg.TransferGasFee == 0 { - return fmt.Errorf("TransferGasFee needs to be above 0. It's the gas fee for a simple transfer transaction") - } - if cfg.TxnTimeout.Duration() == 0 { - return fmt.Errorf("TxnTimeout needs to be above 0. It's the timeout for a transaction") - } - if cfg.EIP1559DynamicFees { - if cfg.GasFeeCap == 0 { - return fmt.Errorf("GasFeeCap needs to be above 0. It's the maximum fee per gas for a transaction (including tip)") - } - if cfg.GasTipCap == 0 { - return fmt.Errorf("GasTipCap needs to be above 0. It's the maximum tip per gas for a transaction") - } - if cfg.GasFeeCap <= cfg.GasTipCap { - return fmt.Errorf("GasFeeCap needs to be above GasTipCap (as it is base fee + tip cap)") - } - } else { - if cfg.GasPrice == 0 { - return fmt.Errorf("GasPrice needs to be above 0. It's the price of gas for a transaction") - } - } - - return nil -} - -const RootKeyNum = 0 - -// AvailableSethKeyNum returns the available Seth address index -// If there are multiple addresses, it will return any synced key -// Otherwise it will return the root key -func AvailableSethKeyNum(client *seth.Client) int { - if len(client.Addresses) > 1 { - return client.AnySyncedKey() - } - return RootKeyNum -} From 1274dd31e0887451354bb203740600ef5fe61eec Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Tue, 28 May 2024 18:49:46 +0200 Subject: [PATCH 17/19] Improve ocr2FeedsTransmitter FromAddress() fallback (#13343) * Improve ocr2FeedsTransmitter FromAddress() fallback * Fix forwarders test error assert --- core/chains/evm/forwarders/forwarder_manager.go | 8 ++++++-- core/chains/evm/forwarders/forwarder_manager_test.go | 6 +++--- core/services/ocrcommon/transmitter.go | 8 ++++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index fca35274708..39ca8236305 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -2,6 +2,7 @@ package forwarders import ( "context" + "errors" "slices" "sync" "time" @@ -129,9 +130,12 @@ func (f *FwdMgr) ForwarderFor(ctx context.Context, addr common.Address) (forward } } } - return common.Address{}, pkgerrors.Errorf("Cannot find forwarder for given EOA") + return common.Address{}, ErrForwarderForEOANotFound } +// ErrForwarderForEOANotFound defines the error triggered when no valid forwarders were found for EOA +var ErrForwarderForEOANotFound = errors.New("cannot find forwarder for given EOA") + func (f *FwdMgr) ForwarderForOCR2Feeds(ctx context.Context, eoa, ocr2Aggregator common.Address) (forwarder common.Address, err error) { fwdrs, err := f.ORM.FindForwardersByChain(ctx, big.Big(*f.evmClient.ConfiguredChainID())) if err != nil { @@ -165,7 +169,7 @@ func (f *FwdMgr) ForwarderForOCR2Feeds(ctx context.Context, eoa, ocr2Aggregator } } } - return common.Address{}, pkgerrors.Errorf("Cannot find forwarder for given EOA") + return common.Address{}, ErrForwarderForEOANotFound } func (f *FwdMgr) ConvertPayload(dest common.Address, origPayload []byte) ([]byte, error) { diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 020446aa547..be8513f5925 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -150,7 +150,7 @@ func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { err = fwdMgr.Start(testutils.Context(t)) require.NoError(t, err) addr, err := fwdMgr.ForwarderFor(ctx, owner.From) - require.ErrorContains(t, err, "Cannot find forwarder for given EOA") + require.ErrorIs(t, err, forwarders.ErrForwarderForEOANotFound) require.True(t, utils.IsZero(addr)) err = fwdMgr.Close() require.NoError(t, err) @@ -216,7 +216,7 @@ func TestFwdMgr_InvalidForwarderForOCR2FeedsStates(t *testing.T) { require.NoError(t, fwdMgr.Start(testutils.Context(t))) // cannot find forwarder because it isn't authorized nor added as a transmitter addr, err := fwdMgr.ForwarderForOCR2Feeds(ctx, owner.From, ocr2Address) - require.ErrorContains(t, err, "Cannot find forwarder for given EOA") + require.ErrorIs(t, err, forwarders.ErrForwarderForEOANotFound) require.True(t, utils.IsZero(addr)) _, err = forwarder.SetAuthorizedSenders(owner, []common.Address{owner.From}) @@ -229,7 +229,7 @@ func TestFwdMgr_InvalidForwarderForOCR2FeedsStates(t *testing.T) { // cannot find forwarder because it isn't added as a transmitter addr, err = fwdMgr.ForwarderForOCR2Feeds(ctx, owner.From, ocr2Address) - require.ErrorContains(t, err, "Cannot find forwarder for given EOA") + require.ErrorIs(t, err, forwarders.ErrForwarderForEOANotFound) require.True(t, utils.IsZero(addr)) onchainConfig, err := testhelpers.GenerateDefaultOCR2OnchainConfig(big.NewInt(0), big.NewInt(10)) diff --git a/core/services/ocrcommon/transmitter.go b/core/services/ocrcommon/transmitter.go index bfb007d1b3e..ccd2b010453 100644 --- a/core/services/ocrcommon/transmitter.go +++ b/core/services/ocrcommon/transmitter.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" ) @@ -173,8 +174,11 @@ func (t *ocr2FeedsTransmitter) FromAddress() common.Address { return t.effectiveTransmitterAddress } - forwarderAddress, err := t.forwarderAddress(context.Background(), roundRobinFromAddress, t.ocr2Aggregator) - if err != nil || forwarderAddress == (common.Address{}) { + forwarderAddress, err := t.GetForwarderForEOAOCR2Feeds(context.Background(), roundRobinFromAddress, t.ocr2Aggregator) + if errors.Is(err, forwarders.ErrForwarderForEOANotFound) { + // if there are no valid forwarders try to fallback to eoa + return roundRobinFromAddress + } else if err != nil { return t.effectiveTransmitterAddress } From fc6db748fd905b0567c465374a64e5ef3ff42750 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Tue, 28 May 2024 18:54:10 +0200 Subject: [PATCH 18/19] Bump starknet relayer (#13345) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 5185278e759..02f51f6ef3c 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -260,7 +260,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.8.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 1e12ac7f3dc..a2c386ee704 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1195,8 +1195,8 @@ github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 h1:ybdconEoRBHLwtDKlZKYaeanQ8UoVqdDiaTlPV+qEiI= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0 h1:5QNKQTQpIp+0ogXUu9B85tEie473RZttOhYxM2vHyOc= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 h1:LQmRsrzzaYYN3wEU1l5tWiccznhvbyGnu2N+wHSXZAo= github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= diff --git a/go.mod b/go.mod index 28e85e2c97c..affee1f60f0 100644 --- a/go.mod +++ b/go.mod @@ -77,7 +77,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 diff --git a/go.sum b/go.sum index 0671117cf0c..5bef3d55d6a 100644 --- a/go.sum +++ b/go.sum @@ -1181,8 +1181,8 @@ github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 h1:ybdconEoRBHLwtDKlZKYaeanQ8UoVqdDiaTlPV+qEiI= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0 h1:5QNKQTQpIp+0ogXUu9B85tEie473RZttOhYxM2vHyOc= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 3a4c0ccfc80..5a1b8592507 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -379,7 +379,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0 // indirect github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f9a7e8a3f12..fd668c30838 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1522,8 +1522,8 @@ github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 h1:ybdconEoRBHLwtDKlZKYaeanQ8UoVqdDiaTlPV+qEiI= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0 h1:5QNKQTQpIp+0ogXUu9B85tEie473RZttOhYxM2vHyOc= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-testing-framework v1.28.17 h1:zezoeiG3GUGW1T2+genS/HD1BvRJwC3rqFnFTFNB9aY= github.com/smartcontractkit/chainlink-testing-framework v1.28.17/go.mod h1:xjxJK+4SUjBmJJWfRFl02poauU4XQE37aH7WYtxTLKg= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 h1:fX/xmGm1GBsD1ZZnooNT+eWA0hiTAqFlHzOC5CY4dy8= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index d81845f4b3c..9ae07767184 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -368,7 +368,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0 // indirect github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 // indirect github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index edcb5c3a5b2..e36f0f2410d 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1512,8 +1512,8 @@ github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20 h1:ybdconEoRBHLwtDKlZKYaeanQ8UoVqdDiaTlPV+qEiI= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240524201401-88d0b3763b20/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128 h1:Kp87qCCWpuz7DUgE4g8mbMuYEMcLYm3TLrKBna2XnoE= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240524210559-72df72854128/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0 h1:5QNKQTQpIp+0ogXUu9B85tEie473RZttOhYxM2vHyOc= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240527073251-56197f1413e0/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-testing-framework v1.28.17 h1:zezoeiG3GUGW1T2+genS/HD1BvRJwC3rqFnFTFNB9aY= github.com/smartcontractkit/chainlink-testing-framework v1.28.17/go.mod h1:xjxJK+4SUjBmJJWfRFl02poauU4XQE37aH7WYtxTLKg= github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 h1:fX/xmGm1GBsD1ZZnooNT+eWA0hiTAqFlHzOC5CY4dy8= From 765a1e8107d6bbff157dea347d8cbec957f11bc9 Mon Sep 17 00:00:00 2001 From: Erik Burton Date: Tue, 28 May 2024 10:57:38 -0700 Subject: [PATCH 19/19] fix: disable solidity-wrappers for dependabot (#13337) --- .github/workflows/solidity-wrappers.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/solidity-wrappers.yml b/.github/workflows/solidity-wrappers.yml index 56528611f36..18f2d4d9f0e 100644 --- a/.github/workflows/solidity-wrappers.yml +++ b/.github/workflows/solidity-wrappers.yml @@ -15,6 +15,8 @@ concurrency: jobs: changes: + # We don't directly merge dependabot PRs, so let's not waste the resources + if: ${{ github.actor != 'dependabot[bot]' }} name: Detect changes runs-on: ubuntu-latest outputs: