From d96c444704b7516ab35cad319214394b26d0a2cb Mon Sep 17 00:00:00 2001 From: Pablo Estrada <139084212+ecPablo@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:36:40 -0600 Subject: [PATCH] feat(deployment): solana ccip transfer ownership to timelock (#16246) Adds the changeset to transfer ownership of CCIP solana programs [DPA-1525](https://smartcontract-it.atlassian.net/browse/DPA-1525) --- .changeset/yellow-hornets-provide.md | 5 + core/scripts/go.mod | 33 ++ core/scripts/go.sum | 89 +---- .../ccip/changeset/cs_chain_contracts.go | 17 +- .../changeset/solana/cs_add_remote_chain.go | 16 +- .../solana/ownership_transfer_helpers.go | 262 +++++++++++++ .../transfer_ccip_to_mcms_with_timelock.go | 218 +++++++++++ ...ransfer_ccip_to_mcms_with_timelock_test.go | 366 ++++++++++++++++++ deployment/ccip/changeset/solana_state.go | 6 +- .../changeset/testhelpers/test_helpers.go | 10 + .../changeset/deploy_link_token_test.go | 4 +- .../changeset/deploy_mcms_with_timelock.go | 7 +- .../deploy_mcms_with_timelock_test.go | 7 +- .../common/changeset/internal/mcms_test.go | 4 +- .../internal/solana/access_controller.go | 26 +- .../common/changeset/internal/solana/mcm.go | 8 +- .../changeset/internal/solana/timelock.go | 8 +- deployment/common/changeset/state/evm.go | 18 + .../{internal/solana => state}/pda.go | 24 +- deployment/common/changeset/state/solana.go | 58 +-- deployment/common/changeset/test_helpers.go | 102 +++++ .../common/proposalutils/mcms_helpers.go | 42 +- .../common/proposalutils/mcms_test_helpers.go | 88 +++-- deployment/environment/memory/chain.go | 44 +++ 24 files changed, 1208 insertions(+), 254 deletions(-) create mode 100644 .changeset/yellow-hornets-provide.md create mode 100644 deployment/ccip/changeset/solana/ownership_transfer_helpers.go create mode 100644 deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock.go create mode 100644 deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go rename deployment/common/changeset/{internal/solana => state}/pda.go (72%) diff --git a/.changeset/yellow-hornets-provide.md b/.changeset/yellow-hornets-provide.md new file mode 100644 index 00000000000..2f374570279 --- /dev/null +++ b/.changeset/yellow-hornets-provide.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal bump core/scripts/go.mod dependencies diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 84f32160752..6d21e54151f 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -56,9 +56,11 @@ require ( require ( cosmossdk.io/errors v1.0.1 // indirect cosmossdk.io/math v1.3.0 // indirect + dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/DataDog/zstd v1.5.2 // indirect @@ -75,6 +77,20 @@ require ( github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c // indirect github.com/avast/retry-go/v4 v4.6.0 // indirect github.com/aws/aws-sdk-go v1.54.19 // indirect + github.com/aws/aws-sdk-go-v2 v1.32.2 // indirect + github.com/aws/aws-sdk-go-v2/config v1.28.0 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.41 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 // indirect + github.com/aws/smithy-go v1.22.0 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect @@ -82,6 +98,7 @@ require ( github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect + github.com/block-vision/sui-go-sdk v1.0.6 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 // indirect github.com/buger/jsonparser v1.1.1 // indirect @@ -106,12 +123,15 @@ require ( github.com/confio/ics23/go v0.9.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect github.com/cosmos/cosmos-sdk v0.47.11 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogoproto v1.4.11 // indirect github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect @@ -168,6 +188,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.24.0 // indirect + github.com/go-resty/resty/v2 v2.15.3 // indirect github.com/go-viper/mapstructure/v2 v2.1.0 // indirect github.com/go-webauthn/webauthn v0.9.4 // indirect github.com/go-webauthn/x v0.1.5 // indirect @@ -249,6 +270,7 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.7.16 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marcboeker/go-duckdb v1.8.3 // indirect @@ -266,8 +288,14 @@ require ( github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/mtibben/percent v0.2.1 // indirect @@ -292,6 +320,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rs/cors v1.10.1 // indirect + github.com/rs/zerolog v1.33.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -303,6 +332,8 @@ require ( github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.24.3 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-salt-fix // indirect github.com/smartcontractkit/chain-selectors v1.0.40 // indirect github.com/smartcontractkit/chainlink-ccip v0.0.0-20250214202341-4190f2db1c01 // indirect @@ -315,6 +346,7 @@ require ( github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 // indirect github.com/smartcontractkit/chainlink-protos/svr v0.0.0-20250123084029-58cce9b32112 // indirect github.com/smartcontractkit/chainlink-solana v1.1.2-0.20250213203720-e15b1333a14a // indirect + github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.7 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/mcms v0.10.0 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect @@ -331,6 +363,7 @@ require ( github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect + github.com/testcontainers/testcontainers-go v0.35.0 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index fef4fea42b3..c2bb39cccf4 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -55,6 +55,8 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI= github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -70,10 +72,6 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= -github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w= -github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM= -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= @@ -94,8 +92,6 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/XSAM/otelsql v0.29.0 h1:pEw9YXXs8ZrGRYfDc0cmArIz9lci5b42gmP5+tA1Huc= github.com/XSAM/otelsql v0.29.0/go.mod h1:d3/0xGIGC5RVEE+Ld7KotwaLy6zDeaF3fLJHOPpdN2w= -github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg= -github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -120,12 +116,8 @@ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c h1:cxQVoh6kY+c4b0HUchHjGWBI8288VhH50qxKG3hdEg0= github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c/go.mod h1:3XzxudkrYVUvbduN/uI2fl4lSrMSzU0+3RCu2mpnfx8= -github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= -github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= -github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= -github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI= github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI= @@ -154,10 +146,6 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 h1:AhmO1fHINP9vFYUE0LHzCWg/ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2/go.mod h1:o8aQygT2+MVP0NaV6kbdE1YnnIM8RRVQzoeUH45GOdI= github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo= github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo= -github.com/aws/constructs-go/constructs/v10 v10.4.2 h1:+hDLTsFGLJmKIn0Dg20vWpKBrVnFrEWYgTEY5UiTEG8= -github.com/aws/constructs-go/constructs/v10 v10.4.2/go.mod h1:cXsNCKDV+9eR9zYYfwy6QuE4uPFp6jsq6TtH1MwBx9w= -github.com/aws/jsii-runtime-go v1.104.0 h1:651Sh6J2FtatfnVzlOQ3/Ye1WWPAseZ6E/tSQxEKdSI= -github.com/aws/jsii-runtime-go v1.104.0/go.mod h1:7ZmQXxV0AAhhvv/GaHX4n6zbgA1tSRVdnQYAJbIhXHk= github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= @@ -175,8 +163,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsy github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/block-vision/sui-go-sdk v1.0.6 h1:FysCc4TJC8v4BEBbCjJPDR4iR5eKqJT1dxGwsT67etg= @@ -201,8 +187,6 @@ github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKz github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 h1:rvc39Ol6z3MvaBzXkxFC6Nfsnixq/dRypushKDd7Nc0= -github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5/go.mod h1:R/pdNYDYFQk+tuuOo7QES1kkv6OLmp5ze2XBZQIVffM= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -216,10 +200,6 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= -github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240821051457-da69c6d9617a h1:6Pg3a6j/41QDzH/oYcMLwwKsf3x/HXcu9W/dBaf2Hzs= -github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240821051457-da69c6d9617a/go.mod h1:x11iCbZV6hzzSQWMq610B6Wl5Lg1dhwqcVfeiWQQnQQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -274,6 +254,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= @@ -305,6 +286,8 @@ github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDF github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= +github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= github.com/cucumber/godog v0.15.0 h1:51AL8lBXF3f0cyA5CV4TnJFCTHpgiy+1x1Hb3TtZUmo= @@ -368,13 +351,6 @@ github.com/ethereum/go-ethereum v1.14.11 h1:8nFDCUUE67rPc6AKxFj7JKaOa2W/W1Rse3oS github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= -github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= -github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= -github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= -github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= -github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= @@ -519,8 +495,6 @@ github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwm github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -579,10 +553,6 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg= -github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= @@ -607,8 +577,6 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -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= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -638,8 +606,6 @@ github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EK github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= @@ -809,8 +775,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52 h1:msKODTL1m0wigztaqILOtla9HeW1ciscYG4xjLtvk5I= github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52/go.mod h1:qk1sX/IBgppQNcGCRoj90u6EGC056EBoIc1oEjCWla8= -github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= @@ -851,8 +815,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/linkedin/goavro/v2 v2.12.0 h1:rIQQSj8jdAUlKQh6DttK8wCRv4t4QO09g1C4aBWXslg= github.com/linkedin/goavro/v2 v2.12.0/go.mod h1:KXx+erlq+RPlGSPmLF7xGo6SAbh8sCQ53x064+ioxhk= github.com/linxGnu/grocksdb v1.7.16 h1:Q2co1xrpdkr5Hx3Fp+f+f7fRGhQFQhvi/+226dtLmA8= @@ -886,6 +848,7 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -932,8 +895,6 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= @@ -949,8 +910,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -963,8 +922,6 @@ github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= @@ -1001,8 +958,6 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= -github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= -github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= @@ -1013,8 +968,6 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= @@ -1069,6 +1022,7 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= @@ -1102,6 +1056,7 @@ github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhV github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -1153,8 +1108,6 @@ github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.7 h1:E7k5 github.com/smartcontractkit/chainlink-testing-framework/framework v0.4.7/go.mod h1:WYxCxAWpeXEHfhB0GaiV2sj21Ooh9r/Nf7tzmJgAibs= github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.22 h1:W3doYLVoZN8VwJb/kAZsbDjW+6cgZPgNTcQHJUH9JrA= github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.22/go.mod h1:70JLBXQncNHyW63ik4PvPQGjQGZ1xK67MKrDanVAk2w= -github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.10 h1:Yf+n3T/fnUWcYyfe7bsygV4sWAkNo0QhN58APJFIKIc= -github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.10/go.mod h1:05duR85P8YHuIfIkA7sn2bvrhKo/pDpFKV2rliYHNOo= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919 h1:IpGoPTXpvllN38kT2z2j13sifJMz4nbHglidvop7mfg= @@ -1278,14 +1231,10 @@ github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= -github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8= -github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= -github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= @@ -1377,8 +1326,6 @@ go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HY go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1614,6 +1561,7 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1622,6 +1570,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1740,8 +1689,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -1867,8 +1814,6 @@ gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= @@ -1910,22 +1855,14 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= -k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= -k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= -k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= -k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= -k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240709000822-3c01b740850f h1:2sXuKesAYbRHxL3aE2PN6zX/gcJr22cjrsej+W784Tc= k8s.io/kube-openapi v0.0.0-20240709000822-3c01b740850f/go.mod h1:UxDHUPsUwTOOxSU+oXURfFBcAS6JwiRXTYqYwfuGowc= -k8s.io/kubectl v0.31.2 h1:gTxbvRkMBwvTSAlobiTVqsH6S8Aa1aGyBcu5xYLsn8M= -k8s.io/kubectl v0.31.2/go.mod h1:EyASYVU6PY+032RrTh5ahtSOMgoDRIux9V1JLKtG5xM= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= @@ -1950,14 +1887,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= -sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= -sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= -sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= -sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= -sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/deployment/ccip/changeset/cs_chain_contracts.go b/deployment/ccip/changeset/cs_chain_contracts.go index c7f1a47f7f6..a05296e0bd7 100644 --- a/deployment/ccip/changeset/cs_chain_contracts.go +++ b/deployment/ccip/changeset/cs_chain_contracts.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_6_0/fee_quoter" "github.com/smartcontractkit/chainlink/deployment" @@ -1358,13 +1359,23 @@ func (c SetOCR3OffRampConfig) validateRemoteChain(e *deployment.Environment, sta } switch family { case chain_selectors.FamilySolana: + chain, ok := e.SolChains[chainSelector] + if !ok { + return fmt.Errorf("chain %d not found in environment", chainSelector) + } chainState, ok := state.SolChains[chainSelector] if !ok { return fmt.Errorf("remote chain %d not found in onchain state", chainSelector) } - - // TODO: introduce interface when MCMS is ready - if err := commoncs.ValidateOwnershipSolana(e.GetContext(), c.MCMS != nil, e.SolChains[chainSelector].DeployerKey.PublicKey(), chainState.Timelock, chainState.Router); err != nil { + addresses, err := e.ExistingAddresses.AddressesForChain(chainSelector) + if err != nil { + return err + } + mcmState, err := commonState.MaybeLoadMCMSWithTimelockChainStateSolana(chain, addresses) + if err != nil { + return fmt.Errorf("error loading MCMS state for chain %d: %w", chainSelector, err) + } + if err := commoncs.ValidateOwnershipSolana(e.GetContext(), c.MCMS != nil, e.SolChains[chainSelector].DeployerKey.PublicKey(), mcmState.TimelockProgram, mcmState.TimelockSeed, chainState.Router); err != nil { return err } case chain_selectors.FamilyEVM: diff --git a/deployment/ccip/changeset/solana/cs_add_remote_chain.go b/deployment/ccip/changeset/solana/cs_add_remote_chain.go index 2053ff32c0d..508f444194b 100644 --- a/deployment/ccip/changeset/solana/cs_add_remote_chain.go +++ b/deployment/ccip/changeset/solana/cs_add_remote_chain.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment" cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" ) // ADD REMOTE CHAIN @@ -55,8 +56,19 @@ func (cfg AddRemoteChainToSolanaConfig) Validate(e deployment.Environment) error if err := validateOffRampConfig(chain, chainState); err != nil { return err } - - if err := commoncs.ValidateOwnershipSolana(e.GetContext(), cfg.MCMS != nil, e.SolChains[cfg.ChainSelector].DeployerKey.PublicKey(), chainState.Timelock, chainState.Router); err != nil { + chain, ok := e.SolChains[cfg.ChainSelector] + if !ok { + return fmt.Errorf("chain %d not found in environment", cfg.ChainSelector) + } + addresses, err := e.ExistingAddresses.AddressesForChain(cfg.ChainSelector) + if err != nil { + return err + } + mcmState, err := commonState.MaybeLoadMCMSWithTimelockChainStateSolana(chain, addresses) + if err != nil { + return fmt.Errorf("error loading MCMS state for chain %d: %w", cfg.ChainSelector, err) + } + if err := commoncs.ValidateOwnershipSolana(e.GetContext(), cfg.MCMS != nil, e.SolChains[cfg.ChainSelector].DeployerKey.PublicKey(), mcmState.TimelockProgram, mcmState.TimelockSeed, chainState.Router); err != nil { return fmt.Errorf("failed to validate ownership: %w", err) } var routerConfigAccount solRouter.Config diff --git a/deployment/ccip/changeset/solana/ownership_transfer_helpers.go b/deployment/ccip/changeset/solana/ownership_transfer_helpers.go new file mode 100644 index 00000000000..07b7c2a5dbe --- /dev/null +++ b/deployment/ccip/changeset/solana/ownership_transfer_helpers.go @@ -0,0 +1,262 @@ +package solana + +import ( + "fmt" + "math/big" + + "github.com/gagliardetto/solana-go" + mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana" + mcmsTypes "github.com/smartcontractkit/mcms/types" + + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/fee_quoter" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" +) + +type TransferOwnershipFn func( + proposedAuthority solana.PublicKey, + configPDA solana.PublicKey, + authority solana.PublicKey, +) (solana.Instruction, error) + +type AcceptOwnershipFn func( + configPDA solana.PublicKey, + authority solana.PublicKey, +) (solana.Instruction, error) + +// transferAndWrapAcceptOwnership abstracts logic of: +// - building a “transfer ownership” instruction +// - confirming on-chain +// - building an “accept ownership” instruction +// - wrapping it in an MCMS transaction +// - returning the mcms transaction for the accept ownership +func transferAndWrapAcceptOwnership( + buildTransfer TransferOwnershipFn, + buildAccept AcceptOwnershipFn, + programID solana.PublicKey, // e.g. token_pool program or router program + proposedOwner solana.PublicKey, // e.g. usually, the timelock signer PDA + configPDA solana.PublicKey, // e.g. for routerConfigPDA or a token-pool config + deployer solana.PublicKey, // the “from” authority + solChain deployment.SolChain, // used for solChain.Confirm + label deployment.ContractType, // e.g. "Router" or "TokenPool" +) (mcmsTypes.Transaction, error) { + // 1. Build the instruction that transfers ownership to the timelock + ixTransfer, err := buildTransfer(proposedOwner, configPDA, deployer) + if err != nil { + return mcmsTypes.Transaction{}, fmt.Errorf("%s: failed to create transfer ownership instruction: %w", label, err) + } + + // 2. Confirm on-chain + if err := solChain.Confirm([]solana.Instruction{ixTransfer}); err != nil { + return mcmsTypes.Transaction{}, fmt.Errorf("%s: failed to confirm transfer on-chain: %w", label, err) + } + + // 3. Build the “accept ownership” instruction + ixAccept, err := buildAccept(configPDA, proposedOwner) + if err != nil { + return mcmsTypes.Transaction{}, fmt.Errorf("%s: failed to create accept ownership instruction: %w", label, err) + } + acceptData, err := ixAccept.Data() + if err != nil { + return mcmsTypes.Transaction{}, fmt.Errorf("%s: failed to extract accept data: %w", label, err) + } + + // 4. Wrap in MCMS transaction + mcmsTx, err := mcmsSolana.NewTransaction( + programID.String(), + acceptData, + big.NewInt(0), // e.g. value + ixAccept.Accounts(), // pass along needed accounts + string(label), // some string identifying the target + []string{}, // any relevant metadata + ) + if err != nil { + return mcmsTypes.Transaction{}, fmt.Errorf("%s: failed to create MCMS transaction: %w", label, err) + } + + return mcmsTx, nil +} + +// transferOwnershipRouter transfers ownership of the router to the timelock. +func transferOwnershipRouter( + ccipState changeset.CCIPOnChainState, + chainSelector uint64, + solChain deployment.SolChain, + timelockProgramID solana.PublicKey, + timelockInstanceSeed state.PDASeed, +) ([]mcmsTypes.Transaction, error) { + var result []mcmsTypes.Transaction + + timelockSignerPDA := state.GetTimelockSignerPDA(timelockProgramID, timelockInstanceSeed) + state := ccipState.SolChains[chainSelector] + + // The relevant on-chain addresses + routerProgramID := state.Router + routerConfigPDA := state.RouterConfigPDA + + // Build specialized closures + buildTransfer := func(newOwner, config, authority solana.PublicKey) (solana.Instruction, error) { + ccip_router.SetProgramID(routerProgramID) + return ccip_router.NewTransferOwnershipInstruction( + newOwner, config, authority, + ).ValidateAndBuild() + } + buildAccept := func(config, newOwnerAuthority solana.PublicKey) (solana.Instruction, error) { + ccip_router.SetProgramID(routerProgramID) + // If the router has its own accept function, use that + ix, err := ccip_router.NewAcceptOwnershipInstruction( + config, newOwnerAuthority, + ).ValidateAndBuild() + if err != nil { + return nil, err + } + for _, acc := range ix.Accounts() { + if acc.PublicKey == newOwnerAuthority { + acc.IsSigner = false + } + } + return ix, nil + } + + tx, err := transferAndWrapAcceptOwnership( + buildTransfer, + buildAccept, + routerProgramID, + timelockSignerPDA, // timelock PDA + routerConfigPDA, // config PDA + solChain.DeployerKey.PublicKey(), + solChain, + changeset.Router, + ) + + if err != nil { + return nil, fmt.Errorf("failed to transfer router ownership: %w", err) + } + + result = append(result, tx) + return result, nil +} + +// transferOwnershipFeeQuoter transfers ownership of the fee quoter to the timelock. +func transferOwnershipFeeQuoter( + ccipState changeset.CCIPOnChainState, + chainSelector uint64, + solChain deployment.SolChain, + timelockProgramID solana.PublicKey, + timelockInstanceSeed state.PDASeed, +) ([]mcmsTypes.Transaction, error) { + var result []mcmsTypes.Transaction + + timelockSignerPDA := state.GetTimelockSignerPDA(timelockProgramID, timelockInstanceSeed) + state := ccipState.SolChains[chainSelector] + + // The relevant on-chain addresses + feeQuoterProgramID := state.FeeQuoter + feeQuoterConfigPDA := state.FeeQuoterConfigPDA + + // Build specialized closures + buildTransfer := func(proposedOwner, config, authority solana.PublicKey) (solana.Instruction, error) { + fee_quoter.SetProgramID(feeQuoterProgramID) + return fee_quoter.NewTransferOwnershipInstruction( + proposedOwner, config, authority, + ).ValidateAndBuild() + } + buildAccept := func(config, newOwnerAuthority solana.PublicKey) (solana.Instruction, error) { + fee_quoter.SetProgramID(feeQuoterProgramID) + // If the router has its own accept function, use that + ix, err := fee_quoter.NewAcceptOwnershipInstruction( + config, newOwnerAuthority, + ).ValidateAndBuild() + if err != nil { + return nil, err + } + for _, acc := range ix.Accounts() { + if acc.PublicKey == newOwnerAuthority { + acc.IsSigner = false + } + } + return ix, nil + } + + tx, err := transferAndWrapAcceptOwnership( + buildTransfer, + buildAccept, + feeQuoterProgramID, + timelockSignerPDA, // timelock PDA + feeQuoterConfigPDA, // config PDA + solChain.DeployerKey.PublicKey(), + solChain, + changeset.FeeQuoter, + ) + + if err != nil { + return nil, fmt.Errorf("failed to transfer fee quoter ownership: %w", err) + } + + result = append(result, tx) + return result, nil +} + +// transferOwnershipOffRamp transfers ownership of the offRamp to the timelock. +func transferOwnershipOffRamp( + ccipState changeset.CCIPOnChainState, + chainSelector uint64, + solChain deployment.SolChain, + timelockProgramID solana.PublicKey, + timelockInstanceSeed state.PDASeed, +) ([]mcmsTypes.Transaction, error) { + var result []mcmsTypes.Transaction + + timelockSignerPDA := state.GetTimelockSignerPDA(timelockProgramID, timelockInstanceSeed) + state := ccipState.SolChains[chainSelector] + + // The relevant on-chain addresses + offRampProgramID := state.OffRamp + offRampConfigPDA := state.OffRampConfigPDA + + // Build specialized closures + buildTransfer := func(proposedOwner, config, authority solana.PublicKey) (solana.Instruction, error) { + ccip_offramp.SetProgramID(offRampProgramID) + return ccip_offramp.NewTransferOwnershipInstruction( + proposedOwner, config, authority, + ).ValidateAndBuild() + } + buildAccept := func(config, newOwnerAuthority solana.PublicKey) (solana.Instruction, error) { + ccip_offramp.SetProgramID(offRampProgramID) + // If the router has its own accept function, use that + ix, err := ccip_offramp.NewAcceptOwnershipInstruction( + config, newOwnerAuthority, + ).ValidateAndBuild() + if err != nil { + return nil, err + } + for _, acc := range ix.Accounts() { + if acc.PublicKey == newOwnerAuthority { + acc.IsSigner = false + } + } + return ix, nil + } + + tx, err := transferAndWrapAcceptOwnership( + buildTransfer, + buildAccept, + offRampProgramID, + timelockSignerPDA, // timelock PDA + offRampConfigPDA, // config PDA + solChain.DeployerKey.PublicKey(), + solChain, + changeset.OffRamp, + ) + + if err != nil { + return nil, fmt.Errorf("failed to transfer offRamp ownership: %w", err) + } + + result = append(result, tx) + return result, nil +} diff --git a/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock.go b/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock.go new file mode 100644 index 00000000000..25fc6e9c90b --- /dev/null +++ b/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock.go @@ -0,0 +1,218 @@ +package solana + +import ( + "errors" + "fmt" + "time" + + "github.com/gagliardetto/solana-go" + chain_selectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/mcms" + "github.com/smartcontractkit/mcms/sdk" + mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana" + mcmsTypes "github.com/smartcontractkit/mcms/types" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" + "github.com/smartcontractkit/chainlink/deployment/common/types" +) + +var _ deployment.ChangeSet[TransferCCIPToMCMSWithTimelockSolanaConfig] = TransferCCIPToMCMSWithTimelockSolana + +// CCIPContractsToTransfer is a struct that represents the contracts we want to transfer. Each contract set to true will be transferred. +type CCIPContractsToTransfer struct { + Router bool + FeeQuoter bool + OffRamp bool +} + +type TransferCCIPToMCMSWithTimelockSolanaConfig struct { + // ContractsByChain is a map of chain selector the contracts we want to transfer. + // Each contract set to true will be transferred + ContractsByChain map[uint64]CCIPContractsToTransfer + // MinDelay is for the accept ownership proposal + MinDelay time.Duration +} + +// ValidateContracts checks if the required contracts are present on the chain +func ValidateContracts(state changeset.SolCCIPChainState, chainSelector uint64, contracts CCIPContractsToTransfer) error { + contractChecks := []struct { + enabled bool + value solana.PublicKey + name string + }{ + {contracts.Router, state.Router, "Router"}, + {contracts.FeeQuoter, state.FeeQuoter, "FeeQuoter"}, + {contracts.OffRamp, state.OffRamp, "OffRamp"}, + } + + for _, check := range contractChecks { + if check.enabled && check.value.IsZero() { + return fmt.Errorf("missing required contract %s on chain %d", check.name, chainSelector) + } + } + + return nil +} + +func (cfg TransferCCIPToMCMSWithTimelockSolanaConfig) Validate(e deployment.Environment) error { + ccipState, err := changeset.LoadOnchainStateSolana(e) + if err != nil { + return fmt.Errorf("failed to load onchain state: %w", err) + } + if len(ccipState.SolChains) == 0 { + return errors.New("no chains found") + } + for chainSelector, contractsEnabled := range cfg.ContractsByChain { + if _, ok := e.SolChains[chainSelector]; !ok { + return fmt.Errorf("chain %d not found in environment", chainSelector) + } + solChain := e.SolChains[chainSelector] + // Load MCM state + addresses, err := e.ExistingAddresses.AddressesForChain(chainSelector) + if err != nil { + return fmt.Errorf("failed to load addresses for chain %d: %w", chainSelector, err) + } + _, err = state.MaybeLoadMCMSWithTimelockChainStateSolana(solChain, addresses) + if err != nil { + return fmt.Errorf("failed to load mcm state: %w", err) + } + chainFamily, err := chain_selectors.GetSelectorFamily(chainSelector) + if err != nil { + return fmt.Errorf("failed to get chain family for chain %d: %w", chainSelector, err) + } + if chainFamily != chain_selectors.FamilySolana { + return fmt.Errorf("chain %d is not a solana chain", chainSelector) + } + state, ok := ccipState.SolChains[chainSelector] + if !ok { + return fmt.Errorf("no state found for chain %d", chainSelector) + } + err = ValidateContracts(state, chainSelector, contractsEnabled) + if err != nil { + return fmt.Errorf("failed to validate contracts for chain %d: %w", chainSelector, err) + } + // If there is no timelock and mcms proposer on the chain, the transfer will fail. + timelockID, err := deployment.SearchAddressBook(e.ExistingAddresses, chainSelector, types.RBACTimelock) + if err != nil { + return fmt.Errorf("timelock not present on the chain %w", err) + } + proposerID, err := deployment.SearchAddressBook(e.ExistingAddresses, chainSelector, types.ProposerManyChainMultisig) + if err != nil { + return fmt.Errorf("mcms proposer not present on the chain %w", err) + } + // Make sure addresses are correctly parsed. Format is: "programID.PDASeed" + _, _, err = mcmsSolana.ParseContractAddress(timelockID) + if err != nil { + return fmt.Errorf("failed to parse timelock address: %w", err) + } + _, _, err = mcmsSolana.ParseContractAddress(proposerID) + if err != nil { + return fmt.Errorf("failed to parse proposer address: %w", err) + } + } + return nil +} + +// TransferCCIPToMCMSWithTimelockSolana creates a changeset that transfers ownership of all the +// CCIP Programs in the provided configuration to the timelock on the chain and generates +// a corresponding proposal with the accept ownership txs to complete the transfer. +// It assumes that DeployMCMSWithTimelock for solana has already been run s.t. +// the timelock and mcms exist on the chain and that the proposed addresses to transfer ownership +// are currently owned by the deployer key. +func TransferCCIPToMCMSWithTimelockSolana( + e deployment.Environment, + cfg TransferCCIPToMCMSWithTimelockSolanaConfig, +) (deployment.ChangesetOutput, error) { + if err := cfg.Validate(e); err != nil { + return deployment.ChangesetOutput{}, err + } + var batches []mcmsTypes.BatchOperation + + ccipState, err := changeset.LoadOnchainStateSolana(e) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to load onchain state: %w", err) + } + + timelocks := map[uint64]string{} + proposers := map[uint64]string{} + inspectors := map[uint64]sdk.Inspector{} + for chainSelector, contractsToTransfer := range cfg.ContractsByChain { + solChain := e.SolChains[chainSelector] + addresses, _ := e.ExistingAddresses.AddressesForChain(chainSelector) + mcmState, _ := state.MaybeLoadMCMSWithTimelockChainStateSolana(solChain, addresses) + + timelocks[solChain.Selector] = mcmsSolana.ContractAddress( + mcmState.TimelockProgram, + mcmsSolana.PDASeed(mcmState.TimelockSeed), + ) + proposers[solChain.Selector] = mcmsSolana.ContractAddress(mcmState.McmProgram, mcmsSolana.PDASeed(mcmState.ProposerMcmSeed)) + inspectors[solChain.Selector] = mcmsSolana.NewInspector(solChain.Client) + if contractsToTransfer.Router { + mcmsTxs, err := transferOwnershipRouter( + ccipState, + chainSelector, + solChain, + mcmState.TimelockProgram, + mcmState.TimelockSeed, + ) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to transfer ownership of router: %w", err) + } + batches = append(batches, mcmsTypes.BatchOperation{ + ChainSelector: mcmsTypes.ChainSelector(chainSelector), + Transactions: mcmsTxs, + }) + } + + if contractsToTransfer.FeeQuoter { + mcmsTxs, err := transferOwnershipFeeQuoter( + ccipState, + chainSelector, + solChain, + mcmState.TimelockProgram, + mcmState.TimelockSeed, + ) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to transfer ownership of fee quoter: %w", err) + } + batches = append(batches, mcmsTypes.BatchOperation{ + ChainSelector: mcmsTypes.ChainSelector(chainSelector), + Transactions: mcmsTxs, + }) + } + + if contractsToTransfer.OffRamp { + mcmsTxs, err := transferOwnershipOffRamp( + ccipState, + chainSelector, + solChain, + mcmState.TimelockProgram, + mcmState.TimelockSeed, + ) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to transfer ownership of offRamp: %w", err) + } + batches = append(batches, mcmsTypes.BatchOperation{ + ChainSelector: mcmsTypes.ChainSelector(chainSelector), + Transactions: mcmsTxs, + }) + } + } + + proposal, err := proposalutils.BuildProposalFromBatchesV2( + e.GetContext(), + timelocks, + proposers, + inspectors, + batches, + "proposal to transfer ownership of CCIP contracts to timelock", + cfg.MinDelay) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to build proposal: %w", err) + } + + return deployment.ChangesetOutput{MCMSTimelockProposals: []mcms.TimelockProposal{*proposal}}, nil +} diff --git a/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go b/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go new file mode 100644 index 00000000000..d1deb8b6ada --- /dev/null +++ b/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go @@ -0,0 +1,366 @@ +package solana_test + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/gagliardetto/solana-go" + chainselectors "github.com/smartcontractkit/chain-selectors" + mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/fee_quoter" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/test_token_pool" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + solanachangesets "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + + commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" +) + +// TODO: remove. These should be deployed as part of the test once deployment changesets are ready. +const TimelockProgramID = "LoCoNsJFuhTkSQjfdDfn3yuwqhSYoPujmviRHVCzsqn" +const MCMProgramID = "6UmMZr5MEqiKWD5jqTJd1WCR5kT8oZuFYBLJFi1o6GQX" + +func TestValidateContracts(t *testing.T) { + validPubkey := solana.NewWallet().PublicKey() + + zeroPubkey := solana.PublicKey{} // Zero public key + + makeState := func(router, feeQuoter solana.PublicKey) changeset.SolCCIPChainState { + return changeset.SolCCIPChainState{ + Router: router, + FeeQuoter: feeQuoter, + } + } + + tests := []struct { + name string + state changeset.SolCCIPChainState + contracts solanachangesets.CCIPContractsToTransfer + chainSelector uint64 + expectedError string + }{ + { + name: "All required contracts present", + state: makeState(validPubkey, validPubkey), + contracts: solanachangesets.CCIPContractsToTransfer{Router: true}, + chainSelector: 12345, + }, + { + name: "Missing Router contract", + state: makeState(zeroPubkey, validPubkey), + contracts: solanachangesets.CCIPContractsToTransfer{Router: true}, + chainSelector: 12345, + expectedError: "missing required contract Router on chain 12345", + }, + { + name: "Missing FeeQuoter contract", + state: makeState(validPubkey, zeroPubkey), + contracts: solanachangesets.CCIPContractsToTransfer{Router: true, FeeQuoter: true}, + chainSelector: 12345, + expectedError: "missing required contract FeeQuoter on chain 12345", + }, + { + name: "invalid pub key", + state: makeState(validPubkey, zeroPubkey), + contracts: solanachangesets.CCIPContractsToTransfer{Router: true, FeeQuoter: true}, + chainSelector: 12345, + expectedError: "missing required contract FeeQuoter on chain 12345", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := solanachangesets.ValidateContracts(tt.state, tt.chainSelector, tt.contracts) + + if tt.expectedError == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Equal(t, tt.expectedError, err.Error()) + } + }) + } +} + +func TestValidate(t *testing.T) { + lggr := logger.TestLogger(t) + env := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Bootstraps: 1, + Chains: 2, + SolChains: 1, + Nodes: 4, + }) + envWithInvalidSolChain := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Bootstraps: 1, + Chains: 2, + SolChains: 1, + Nodes: 4, + }) + envWithInvalidSolChain.SolChains[chainselectors.ETHEREUM_TESTNET_SEPOLIA_LENS_1.Selector] = deployment.SolChain{} + timelockID := mcmsSolana.ContractAddress(solana.MustPublicKeyFromBase58(TimelockProgramID), [32]byte{'t', 'e', 's', 't'}) + mcmsID := mcmsSolana.ContractAddress(solana.MustPublicKeyFromBase58(MCMProgramID), [32]byte{'t', 'e', 's', 't'}) + err := env.ExistingAddresses.Save(env.AllChainSelectorsSolana()[0], timelockID, deployment.TypeAndVersion{Type: commontypes.RBACTimelock, Version: deployment.Version1_0_0}) + require.NoError(t, err) + err = env.ExistingAddresses.Save(env.AllChainSelectorsSolana()[0], mcmsID, deployment.TypeAndVersion{Type: commontypes.ProposerManyChainMultisig, Version: deployment.Version1_0_0}) + require.NoError(t, err) + + tests := []struct { + name string + env deployment.Environment + contractsByChain map[uint64]solanachangesets.CCIPContractsToTransfer + expectedError string + }{ + { + name: "No chains found in environment", + env: memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Bootstraps: 1, + Chains: 0, + SolChains: 0, + Nodes: 4, + }), + expectedError: "no chains found", + }, + { + name: "Chain selector not found in environment", + env: memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Bootstraps: 1, + Chains: 1, + SolChains: 1, + Nodes: 4, + }), + contractsByChain: map[uint64]solanachangesets.CCIPContractsToTransfer{ + 99999: {Router: true, FeeQuoter: true}, + }, + expectedError: "chain 99999 not found in environment", + }, + { + name: "Invalid chain family", + env: envWithInvalidSolChain, + contractsByChain: map[uint64]solanachangesets.CCIPContractsToTransfer{ + chainselectors.ETHEREUM_TESTNET_SEPOLIA_LENS_1.Selector: {Router: true, FeeQuoter: true}, + }, + expectedError: "failed to load addresses for chain 6827576821754315911: chain selector 6827576821754315911: chain not found", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := solanachangesets.TransferCCIPToMCMSWithTimelockSolanaConfig{ + ContractsByChain: tt.contractsByChain, + MinDelay: 10 * time.Second, + } + + err := cfg.Validate(tt.env) + + if tt.expectedError == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedError) + } + }) + } +} + +// prepareEnvironmentForOwnershipTransfer helper that deploys the necessary contracts as pre-requisite to +// the transfer ownership changeset. +func prepareEnvironmentForOwnershipTransfer(t *testing.T) (deployment.Environment, changeset.CCIPOnChainState) { + t.Helper() + lggr := logger.TestLogger(t) + e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Bootstraps: 1, + Chains: 2, + SolChains: 1, + Nodes: 4, + }) + evmSelectors := e.AllChainSelectors() + homeChainSel := evmSelectors[0] + solChainSelectors := e.AllChainSelectorsSolana() + solChain1 := e.AllChainSelectorsSolana()[0] + solChain := e.SolChains[solChain1] + selectors := make([]uint64, 0, len(evmSelectors)+len(solChainSelectors)) + selectors = append(selectors, evmSelectors...) + selectors = append(selectors, solChainSelectors...) + nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) + require.NoError(t, err) + // Fund account for fees + testutils.FundAccounts(e.GetContext(), []solana.PrivateKey{*solChain.DeployerKey}, solChain.Client, t) + cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + contractParams := make(map[uint64]changeset.ChainContractParams) + for _, chain := range solChainSelectors { + contractParams[chain] = changeset.ChainContractParams{ + FeeQuoterParams: changeset.DefaultFeeQuoterParams(), + OffRampParams: changeset.DefaultOffRampParams(), + } + } + prereqCfg := make([]changeset.DeployPrerequisiteConfigPerChain, 0) + for _, chain := range e.AllChainSelectors() { + prereqCfg = append(prereqCfg, changeset.DeployPrerequisiteConfigPerChain{ + ChainSelector: chain, + }) + } + testhelpers.SavePreloadedSolAddresses(t, e, solChainSelectors[0]) + e, err = commonchangeset.ApplyChangesets(t, e, nil, []commonchangeset.ConfiguredChangeSet{ + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(changeset.DeployHomeChainChangeset), + changeset.DeployHomeChainConfig{ + HomeChainSel: homeChainSel, + RMNStaticConfig: testhelpers.NewTestRMNStaticConfig(), + RMNDynamicConfig: testhelpers.NewTestRMNDynamicConfig(), + NodeOperators: testhelpers.NewTestNodeOperator(e.Chains[homeChainSel].DeployerKey.From), + NodeP2PIDsPerNodeOpAdmin: map[string][][32]byte{ + testhelpers.TestNodeOperator: nodes.NonBootstraps().PeerIDs(), + }, + }, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(commonchangeset.DeployLinkToken), + selectors, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(commonchangeset.DeployMCMSWithTimelock), + cfg, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(changeset.DeployPrerequisitesChangeset), + changeset.DeployPrerequisiteConfig{ + Configs: prereqCfg, + }, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(solanachangesets.DeployChainContractsChangesetSolana), + changeset.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: contractParams, + }, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(solanachangesets.DeploySolanaToken), + solanachangesets.DeploySolanaTokenConfig{ + ChainSelector: solChain1, + TokenProgramName: deployment.SPL2022Tokens, + TokenDecimals: 9, + }, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(commonchangeset.DeployMCMSWithTimelockV2), + map[uint64]commontypes.MCMSWithTimelockConfigV2{ + solChain1: { + Canceller: proposalutils.SingleGroupMCMSV2(t), + Proposer: proposalutils.SingleGroupMCMSV2(t), + Bypasser: proposalutils.SingleGroupMCMSV2(t), + TimelockMinDelay: big.NewInt(0), + }, + }, + ), + }) + require.NoError(t, err) + + // solana verification + testhelpers.ValidateSolanaState(t, e, solChainSelectors) + state, err := changeset.LoadOnchainStateSolana(e) + require.NoError(t, err) + tokenAddress := state.SolChains[solChain1].SPL2022Tokens[0] + + e, err = commonchangeset.ApplyChangesets(t, e, nil, []commonchangeset.ConfiguredChangeSet{ + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(solanachangesets.AddTokenPool), + solanachangesets.TokenPoolConfig{ + ChainSelector: solChain1, + TokenPubKey: tokenAddress.String(), + TokenProgramName: deployment.SPL2022Tokens, + PoolType: test_token_pool.LockAndRelease_PoolType, + Authority: e.SolChains[solChain1].DeployerKey.PublicKey().String(), + }, + ), + }) + require.NoError(t, err) + return e, state +} +func TestTransferCCIPToMCMSWithTimelockSolana(t *testing.T) { + t.Parallel() + e, state := prepareEnvironmentForOwnershipTransfer(t) + solChain1 := e.AllChainSelectorsSolana()[0] + solChain := e.SolChains[solChain1] + // tokenAddress := state.SolChains[solChain1].SPL2022Tokens[0] + addresses, err := e.ExistingAddresses.AddressesForChain(solChain1) + require.NoError(t, err) + mcmState, err := commonState.MaybeLoadMCMSWithTimelockChainStateSolana(e.SolChains[solChain1], addresses) + require.NoError(t, err) + + // Fund signer PDAs for timelock and mcm + // If we don't fund, execute() calls will fail with "no funds" errors. + timelockSignerPDA := commonState.GetTimelockSignerPDA(mcmState.TimelockProgram, mcmState.TimelockSeed) + mcmSignerPDA := commonState.GetMCMSignerPDA(mcmState.McmProgram, mcmState.ProposerMcmSeed) + memory.FundSolanaAccounts(e.GetContext(), t, []solana.PublicKey{timelockSignerPDA, mcmSignerPDA}, + 100, solChain.Client) + t.Logf("funded timelock signer PDA: %s", timelockSignerPDA.String()) + t.Logf("funded mcm signer PDA: %s", mcmSignerPDA.String()) + // Apply transfer ownership changeset + e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(solanachangesets.TransferCCIPToMCMSWithTimelockSolana), + solanachangesets.TransferCCIPToMCMSWithTimelockSolanaConfig{ + MinDelay: 1 * time.Second, + ContractsByChain: map[uint64]solanachangesets.CCIPContractsToTransfer{ + solChain1: { + Router: true, + FeeQuoter: true, + OffRamp: true, + }, + }, + }, + ), + }) + require.NoError(t, err) + + // 5. Now verify on-chain that each contract’s “config account” authority is the Timelock PDA. + // Typically, each contract has its own config account: RouterConfigPDA, FeeQuoterConfigPDA, + // Token Pool config PDAs, OffRamp config, etc. + ctx := context.Background() + + // (A) Check Router ownership - we need to add retries as the ownership transfer commitment is confirmed and not finalized. + require.Eventually(t, func() bool { + routerConfigPDA := state.SolChains[solChain1].RouterConfigPDA + t.Logf("Checking Router Config PDA ownership data configPDA: %s", routerConfigPDA.String()) + programData := ccip_router.Config{} + err = solChain.GetAccountDataBorshInto(ctx, routerConfigPDA, &programData) + return timelockSignerPDA.String() == programData.Owner.String() + }, 30*time.Second, 5*time.Second, "Router config PDA owner was not changed to timelock signer PDA") + + // (B) Check FeeQuoter ownership + require.Eventually(t, func() bool { + feeQuoterConfigPDA := state.SolChains[solChain1].FeeQuoterConfigPDA + t.Logf("Checking Fee Quoter PDA ownership data configPDA: %s", feeQuoterConfigPDA.String()) + programData := fee_quoter.Config{} + err = solChain.GetAccountDataBorshInto(ctx, feeQuoterConfigPDA, &programData) + require.NoError(t, err) + return timelockSignerPDA.String() == programData.Owner.String() + }, 30*time.Second, 5*time.Second, "Fee Quoter config PDA owner was not changed to timelock signer PDA") + + // (C) Check OffRamp: + require.Eventually(t, func() bool { + offRampConfigPDA := state.SolChains[solChain1].OffRampConfigPDA + programData := ccip_offramp.Config{} + t.Logf("Checking Off Ramp PDA ownership data configPDA: %s", offRampConfigPDA.String()) + err = solChain.GetAccountDataBorshInto(ctx, offRampConfigPDA, &programData) + require.NoError(t, err) + return timelockSignerPDA.String() == programData.Owner.String() + }, 30*time.Second, 5*time.Second, "OffRamp config PDA owner was not changed to timelock signer PDA") +} diff --git a/deployment/ccip/changeset/solana_state.go b/deployment/ccip/changeset/solana_state.go index eb524fb3dce..06e0e449dac 100644 --- a/deployment/ccip/changeset/solana_state.go +++ b/deployment/ccip/changeset/solana_state.go @@ -2,10 +2,10 @@ package changeset import ( "errors" - "fmt" "strconv" "github.com/gagliardetto/solana-go" + "github.com/rs/zerolog/log" solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" @@ -32,7 +32,6 @@ var ( type SolCCIPChainState struct { LinkToken solana.PublicKey Router solana.PublicKey - Timelock solana.PublicKey OfframpAddressLookupTable solana.PublicKey Receiver solana.PublicKey // for tests only SPL2022Tokens []solana.PublicKey @@ -154,7 +153,8 @@ func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deploy } state.OffRampStatePDA = offRampStatePDA default: - return state, fmt.Errorf("unknown contract %s", tvStr) + log.Warn().Str("address", address).Str("type", string(tvStr.Type)).Msg("Unknown address type") + continue } } state.WSOL = solana.SolMint diff --git a/deployment/ccip/changeset/testhelpers/test_helpers.go b/deployment/ccip/changeset/testhelpers/test_helpers.go index 17934465b0a..45289e68b37 100644 --- a/deployment/ccip/changeset/testhelpers/test_helpers.go +++ b/deployment/ccip/changeset/testhelpers/test_helpers.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" changeset_solana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_6_0/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/services/relay" @@ -1506,6 +1507,15 @@ func SavePreloadedSolAddresses(t *testing.T, e deployment.Environment, solChainS tv = deployment.NewTypeAndVersion(changeset.OffRamp, deployment.Version1_0_0) err = e.ExistingAddresses.Save(solChainSelector, solTestConfig.CcipOfframpProgram.String(), tv) require.NoError(t, err) + tv = deployment.NewTypeAndVersion(commontypes.ManyChainMultisigProgram, deployment.Version1_0_0) + err = e.ExistingAddresses.Save(solChainSelector, memory.SolanaProgramIDs["mcm"], tv) + require.NoError(t, err) + tv = deployment.NewTypeAndVersion(commontypes.AccessControllerProgram, deployment.Version1_0_0) + err = e.ExistingAddresses.Save(solChainSelector, memory.SolanaProgramIDs["access_controller"], tv) + require.NoError(t, err) + tv = deployment.NewTypeAndVersion(commontypes.RBACTimelockProgram, deployment.Version1_0_0) + err = e.ExistingAddresses.Save(solChainSelector, memory.SolanaProgramIDs["timelock"], tv) + require.NoError(t, err) } func ValidateSolanaState(t *testing.T, e deployment.Environment, solChainSelectors []uint64) { diff --git a/deployment/common/changeset/deploy_link_token_test.go b/deployment/common/changeset/deploy_link_token_test.go index 1491481a0fe..7b15dd7bc9c 100644 --- a/deployment/common/changeset/deploy_link_token_test.go +++ b/deployment/common/changeset/deploy_link_token_test.go @@ -3,10 +3,10 @@ package changeset_test import ( "testing" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" + "github.com/smartcontractkit/chainlink/deployment/common/changeset" ) func TestDeployLinkToken(t *testing.T) { t.Parallel() - testhelpers.DeployLinkTokenTest(t, 0) + changeset.DeployLinkTokenTest(t, 0) } diff --git a/deployment/common/changeset/deploy_mcms_with_timelock.go b/deployment/common/changeset/deploy_mcms_with_timelock.go index 02c4db8fb59..cfe77a23c08 100644 --- a/deployment/common/changeset/deploy_mcms_with_timelock.go +++ b/deployment/common/changeset/deploy_mcms_with_timelock.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal" evminternal "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal/evm" solanainternal "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal/solana" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/types" ) @@ -82,6 +83,10 @@ func ValidateOwnership(ctx context.Context, mcms bool, deployerKey, timelock com } // TODO: SOLANA_CCIP -func ValidateOwnershipSolana(ctx context.Context, mcms bool, deployerKey, timelock, ccipRouter solana.PublicKey) error { +func ValidateOwnershipSolana( + ctx context.Context, mcms bool, deployerKey, timelock solana.PublicKey, timelockSeed state.PDASeed, + ccipRouter solana.PublicKey, +) error { + // TODO: implement return nil } diff --git a/deployment/common/changeset/deploy_mcms_with_timelock_test.go b/deployment/common/changeset/deploy_mcms_with_timelock_test.go index d5f1a8c4749..77e33ed42a3 100644 --- a/deployment/common/changeset/deploy_mcms_with_timelock_test.go +++ b/deployment/common/changeset/deploy_mcms_with_timelock_test.go @@ -18,7 +18,6 @@ import ( timelockBindings "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" "github.com/smartcontractkit/chainlink/deployment" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" - solanainternal "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal/solana" mcmschangesetstate "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/memory" @@ -269,11 +268,11 @@ func TestDeployMCMSWithTimelockV2(t *testing.T) { // ----- helpers ----- func mcmSignerPDA(programID solana.PublicKey, seed mcmschangesetstate.PDASeed) string { - return solanainternal.GetMCMSignerPDA(programID, seed).String() + return mcmschangesetstate.GetMCMSignerPDA(programID, seed).String() } func timelockSignerPDA(programID solana.PublicKey, seed mcmschangesetstate.PDASeed) string { - return solanainternal.GetTimelockSignerPDA(programID, seed).String() + return mcmschangesetstate.GetTimelockSignerPDA(programID, seed).String() } func setPreloadedSolanaAddresses(t *testing.T, env deployment.Environment, selector uint64) { @@ -296,7 +295,7 @@ func solanaTimelockConfig( t.Helper() var data timelockBindings.Config - err := chain.GetAccountDataBorshInto(ctx, solanainternal.GetTimelockConfigPDA(programID, seed), &data) + err := chain.GetAccountDataBorshInto(ctx, mcmschangesetstate.GetTimelockConfigPDA(programID, seed), &data) require.NoError(t, err) return data diff --git a/deployment/common/changeset/internal/mcms_test.go b/deployment/common/changeset/internal/mcms_test.go index f404a645851..33e46e7c880 100644 --- a/deployment/common/changeset/internal/mcms_test.go +++ b/deployment/common/changeset/internal/mcms_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/memory" @@ -63,7 +63,7 @@ func TestDeployMCMSWithTimelockContracts(t *testing.T) { addresses, err := ab.AddressesForChain(chainsel.TEST_90000001.Selector) require.NoError(t, err) require.Len(t, addresses, 5) - mcmsState, err := changeset.MaybeLoadMCMSWithTimelockChainState(chains[chainsel.TEST_90000001.Selector], addresses) + mcmsState, err := state.MaybeLoadMCMSWithTimelockChainState(chains[chainsel.TEST_90000001.Selector], addresses) require.NoError(t, err) v, err := mcmsState.GenerateMCMSWithTimelockView() b, err := json.MarshalIndent(v, "", " ") diff --git a/deployment/common/changeset/internal/solana/access_controller.go b/deployment/common/changeset/internal/solana/access_controller.go index 4975187c2d6..390da5bba51 100644 --- a/deployment/common/changeset/internal/solana/access_controller.go +++ b/deployment/common/changeset/internal/solana/access_controller.go @@ -12,6 +12,7 @@ import ( timelockBindings "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" solanaUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" @@ -82,13 +83,12 @@ func initAccessController( } log.Infow("initialized access controller", "account", account.PublicKey()) - address := state.EncodeAddressWithAccount(programID, account.PublicKey()) - err = addressBook.Save(chain.Selector, address, typeAndVersion) + err = addressBook.Save(chain.Selector, account.PublicKey().String(), typeAndVersion) if err != nil { return fmt.Errorf("failed to save address: %w", err) } - err = chainState.SetState(contractType, programID, state.PDASeed(account.PublicKey())) + err = chainState.SetState(contractType, account.PublicKey(), state.PDASeed{}) if err != nil { return fmt.Errorf("failed to save onchain state: %w", err) } @@ -100,7 +100,7 @@ func initAccessController( const accessControllerAccountSize = uint64(8 + 32 + 32 + ((32 * 64) + 8)) func initializeAccessController( - e deployment.Environment, chain deployment.SolChain, programID solana.PublicKey, account solana.PrivateKey, + e deployment.Environment, chain deployment.SolChain, programID solana.PublicKey, roleAccount solana.PrivateKey, ) error { rentExemption, err := chain.Client.GetMinimumBalanceForRentExemption(e.GetContext(), accessControllerAccountSize, rpc.CommitmentConfirmed) @@ -109,13 +109,13 @@ func initializeAccessController( } createAccountInstruction, err := system.NewCreateAccountInstruction(rentExemption, accessControllerAccountSize, - programID, chain.DeployerKey.PublicKey(), account.PublicKey()).ValidateAndBuild() + programID, chain.DeployerKey.PublicKey(), roleAccount.PublicKey()).ValidateAndBuild() if err != nil { return fmt.Errorf("failed to create CreateAccount instruction: %w", err) } initializeInstruction, err := accessControllerBindings.NewInitializeInstruction( - account.PublicKey(), + roleAccount.PublicKey(), chain.DeployerKey.PublicKey(), ).ValidateAndBuild() if err != nil { @@ -123,24 +123,24 @@ func initializeAccessController( } instructions := []solana.Instruction{createAccountInstruction, initializeInstruction} - err = chain.Confirm(instructions, solanaUtils.AddSigners(account)) + err = chain.Confirm(instructions, solanaUtils.AddSigners(roleAccount)) if err != nil { return fmt.Errorf("failed to confirm CreateAccount and InitializeAccessController instructions: %w", err) } var data accessControllerBindings.AccessController - err = solanaUtils.GetAccountDataBorshInto(e.GetContext(), chain.Client, account.PublicKey(), rpc.CommitmentConfirmed, &data) + err = solanaUtils.GetAccountDataBorshInto(e.GetContext(), chain.Client, roleAccount.PublicKey(), rpc.CommitmentConfirmed, &data) if err != nil { - return fmt.Errorf("failed to read access controller account: %w", err) + return fmt.Errorf("failed to read access controller roleAccount: %w", err) } return nil } func setupRoles(chainState *state.MCMSWithTimelockStateSolana, chain deployment.SolChain) error { - proposerPDA := GetMCMSignerPDA(chainState.McmProgram, chainState.ProposerMcmSeed) - cancellerPDA := GetMCMSignerPDA(chainState.McmProgram, chainState.CancellerMcmSeed) - bypasserPDA := GetMCMSignerPDA(chainState.McmProgram, chainState.BypasserMcmSeed) + proposerPDA := state.GetMCMSignerPDA(chainState.McmProgram, chainState.ProposerMcmSeed) + cancellerPDA := state.GetMCMSignerPDA(chainState.McmProgram, chainState.CancellerMcmSeed) + bypasserPDA := state.GetMCMSignerPDA(chainState.McmProgram, chainState.BypasserMcmSeed) err := addAccess(chain, chainState, timelockBindings.Proposer_Role, proposerPDA) if err != nil { @@ -169,7 +169,7 @@ func addAccess( chain deployment.SolChain, chainState *state.MCMSWithTimelockStateSolana, role timelockBindings.Role, accounts ...solana.PublicKey, ) error { - timelockConfigPDA := GetTimelockConfigPDA(chainState.TimelockProgram, chainState.TimelockSeed) + timelockConfigPDA := state.GetTimelockConfigPDA(chainState.TimelockProgram, chainState.TimelockSeed) instructionBuilder := timelockBindings.NewBatchAddAccessInstruction([32]uint8(chainState.TimelockSeed), role, timelockConfigPDA, chainState.AccessControllerProgram, chainState.RoleAccount(role), chain.DeployerKey.PublicKey()) diff --git a/deployment/common/changeset/internal/solana/mcm.go b/deployment/common/changeset/internal/solana/mcm.go index 6e6332bbe6c..7c9bf58bf24 100644 --- a/deployment/common/changeset/internal/solana/mcm.go +++ b/deployment/common/changeset/internal/solana/mcm.go @@ -104,7 +104,7 @@ func initMCM( func initializeMCM(e deployment.Environment, chain deployment.SolChain, mcmProgram solana.PublicKey, multisigID state.PDASeed) error { var mcmConfig mcmBindings.MultisigConfig - err := chain.GetAccountDataBorshInto(e.GetContext(), GetMCMConfigPDA(mcmProgram, multisigID), &mcmConfig) + err := chain.GetAccountDataBorshInto(e.GetContext(), state.GetMCMConfigPDA(mcmProgram, multisigID), &mcmConfig) if err == nil { e.Logger.Infow("MCM already initialized, skipping initialization", "chain", chain.String()) return nil @@ -128,13 +128,13 @@ func initializeMCM(e deployment.Environment, chain deployment.SolChain, mcmProgr instruction, err := mcmBindings.NewInitializeInstruction( chain.Selector, multisigID, - GetMCMConfigPDA(mcmProgram, multisigID), + state.GetMCMConfigPDA(mcmProgram, multisigID), chain.DeployerKey.PublicKey(), solana.SystemProgramID, mcmProgram, programData.Address, - GetMCMRootMetadataPDA(mcmProgram, multisigID), - GetMCMExpiringRootAndOpCountPDA(mcmProgram, multisigID), + state.GetMCMRootMetadataPDA(mcmProgram, multisigID), + state.GetMCMExpiringRootAndOpCountPDA(mcmProgram, multisigID), ).ValidateAndBuild() if err != nil { return fmt.Errorf("failed to build instruction: %w", err) diff --git a/deployment/common/changeset/internal/solana/timelock.go b/deployment/common/changeset/internal/solana/timelock.go index 043bd0a2494..3eaded54ebb 100644 --- a/deployment/common/changeset/internal/solana/timelock.go +++ b/deployment/common/changeset/internal/solana/timelock.go @@ -102,7 +102,7 @@ func initializeTimelock( } var timelockConfig timelockBindings.Config - err := chain.GetAccountDataBorshInto(e.GetContext(), GetTimelockConfigPDA(timelockProgram, timelockID), + err := chain.GetAccountDataBorshInto(e.GetContext(), state.GetTimelockConfigPDA(timelockProgram, timelockID), &timelockConfig) if err == nil { e.Logger.Infow("Timelock already initialized, skipping initialization", "chain", chain.String()) @@ -127,7 +127,7 @@ func initializeTimelock( instruction, err := timelockBindings.NewInitializeInstruction( timelockID, minDelay.Uint64(), - GetTimelockConfigPDA(timelockProgram, timelockID), + state.GetTimelockConfigPDA(timelockProgram, timelockID), chain.DeployerKey.PublicKey(), solana.SystemProgramID, timelockProgram, @@ -152,8 +152,8 @@ func initializeTimelock( func transferOwnershipTimelock(chain deployment.SolChain, programID solana.PublicKey, seed state.PDASeed) error { // transfer timelock ownership to itself - timelockConfigPDA := GetTimelockConfigPDA(programID, seed) - timelockSignerPDA := GetTimelockSignerPDA(programID, seed) + timelockConfigPDA := state.GetTimelockConfigPDA(programID, seed) + timelockSignerPDA := state.GetTimelockSignerPDA(programID, seed) instructionBuilder := timelockBindings.NewTransferOwnershipInstruction([32]uint8(seed), timelockSignerPDA, timelockConfigPDA, chain.DeployerKey.PublicKey()) diff --git a/deployment/common/changeset/state/evm.go b/deployment/common/changeset/state/evm.go index aa3dc189972..d1319cc2d89 100644 --- a/deployment/common/changeset/state/evm.go +++ b/deployment/common/changeset/state/evm.go @@ -94,6 +94,8 @@ func MaybeLoadMCMSWithTimelockChainState(chain deployment.Chain, addresses map[s proposer := deployment.NewTypeAndVersion(types.ProposerManyChainMultisig, deployment.Version1_0_0) canceller := deployment.NewTypeAndVersion(types.CancellerManyChainMultisig, deployment.Version1_0_0) bypasser := deployment.NewTypeAndVersion(types.BypasserManyChainMultisig, deployment.Version1_0_0) + // the same contract can have different roles + multichain := deployment.NewTypeAndVersion(types.ManyChainMultisig, deployment.Version1_0_0) // Convert map keys to a slice wantTypes := []deployment.TypeAndVersion{timelock, proposer, canceller, bypasser, callProxy} @@ -136,7 +138,23 @@ func MaybeLoadMCMSWithTimelockChainState(chain deployment.Chain, addresses map[s return nil, err } state.CancellerMcm = mcms + case tvStr.Type == multichain.Type && tvStr.Version.String() == multichain.Version.String(): + // the same contract can have different roles so we use the labels to determine which role it is + mcms, err := bindings.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) + if err != nil { + return nil, err + } + if tvStr.Labels.Contains(types.ProposerRole.String()) { + state.ProposerMcm = mcms + } + if tvStr.Labels.Contains(types.BypasserRole.String()) { + state.BypasserMcm = mcms + } + if tvStr.Labels.Contains(types.CancellerRole.String()) { + state.CancellerMcm = mcms + } } + } return &state, nil } diff --git a/deployment/common/changeset/internal/solana/pda.go b/deployment/common/changeset/state/pda.go similarity index 72% rename from deployment/common/changeset/internal/solana/pda.go rename to deployment/common/changeset/state/pda.go index c0255a45dcf..148aa35d25e 100644 --- a/deployment/common/changeset/internal/solana/pda.go +++ b/deployment/common/changeset/state/pda.go @@ -1,12 +1,10 @@ -package mcmsnew +package state import ( "encoding/binary" "github.com/ethereum/go-ethereum/common" "github.com/gagliardetto/solana-go" - - "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" ) func getPDA(programID solana.PublicKey, seeds [][]byte) solana.PublicKey { @@ -14,56 +12,56 @@ func getPDA(programID solana.PublicKey, seeds [][]byte) solana.PublicKey { return pda } -func GetMCMSignerPDA(programID solana.PublicKey, msigID state.PDASeed) solana.PublicKey { +func GetMCMSignerPDA(programID solana.PublicKey, msigID PDASeed) solana.PublicKey { seeds := [][]byte{[]byte("multisig_signer"), msigID[:]} return getPDA(programID, seeds) } -func GetMCMConfigPDA(programID solana.PublicKey, msigID state.PDASeed) solana.PublicKey { +func GetMCMConfigPDA(programID solana.PublicKey, msigID PDASeed) solana.PublicKey { seeds := [][]byte{[]byte("multisig_config"), msigID[:]} return getPDA(programID, seeds) } -func GetMCMRootMetadataPDA(programID solana.PublicKey, msigID state.PDASeed) solana.PublicKey { +func GetMCMRootMetadataPDA(programID solana.PublicKey, msigID PDASeed) solana.PublicKey { seeds := [][]byte{[]byte("root_metadata"), msigID[:]} return getPDA(programID, seeds) } -func GetMCMExpiringRootAndOpCountPDA(programID solana.PublicKey, pdaSeed state.PDASeed) solana.PublicKey { +func GetMCMExpiringRootAndOpCountPDA(programID solana.PublicKey, pdaSeed PDASeed) solana.PublicKey { seeds := [][]byte{[]byte("expiring_root_and_op_count"), pdaSeed[:]} return getPDA(programID, seeds) } func GetMCMRootSignaturesPDA( - programID solana.PublicKey, msigID state.PDASeed, root common.Hash, validUntil uint32, + programID solana.PublicKey, msigID PDASeed, root common.Hash, validUntil uint32, ) solana.PublicKey { seeds := [][]byte{[]byte("root_signatures"), msigID[:], root[:], validUntilBytes(validUntil)} return getPDA(programID, seeds) } func GetMCMSeenSignedHashesPDA( - programID solana.PublicKey, msigID state.PDASeed, root common.Hash, validUntil uint32, + programID solana.PublicKey, msigID PDASeed, root common.Hash, validUntil uint32, ) solana.PublicKey { seeds := [][]byte{[]byte("seen_signed_hashes"), msigID[:], root[:], validUntilBytes(validUntil)} return getPDA(programID, seeds) } -func GetTimelockConfigPDA(programID solana.PublicKey, timelockID state.PDASeed) solana.PublicKey { +func GetTimelockConfigPDA(programID solana.PublicKey, timelockID PDASeed) solana.PublicKey { seeds := [][]byte{[]byte("timelock_config"), timelockID[:]} return getPDA(programID, seeds) } -func GetTimelockOperationPDA(programID solana.PublicKey, timelockID state.PDASeed, opID [32]byte) solana.PublicKey { +func GetTimelockOperationPDA(programID solana.PublicKey, timelockID PDASeed, opID [32]byte) solana.PublicKey { seeds := [][]byte{[]byte("timelock_operation"), timelockID[:], opID[:]} return getPDA(programID, seeds) } -func GetTimelockBypasserOperationPDA(programID solana.PublicKey, timelockID state.PDASeed, opID [32]byte) solana.PublicKey { +func GetTimelockBypasserOperationPDA(programID solana.PublicKey, timelockID PDASeed, opID [32]byte) solana.PublicKey { seeds := [][]byte{[]byte("timelock_bypasser_operation"), timelockID[:], opID[:]} return getPDA(programID, seeds) } -func GetTimelockSignerPDA(programID solana.PublicKey, timelockID state.PDASeed) solana.PublicKey { +func GetTimelockSignerPDA(programID solana.PublicKey, timelockID PDASeed) solana.PublicKey { seeds := [][]byte{[]byte("timelock_signer"), timelockID[:]} return getPDA(programID, seeds) } diff --git a/deployment/common/changeset/state/solana.go b/deployment/common/changeset/state/solana.go index 1733c41eef7..fecb86bcffc 100644 --- a/deployment/common/changeset/state/solana.go +++ b/deployment/common/changeset/state/solana.go @@ -3,7 +3,6 @@ package state import ( "errors" "fmt" - "strings" "github.com/gagliardetto/solana-go" mcmssolanasdk "github.com/smartcontractkit/mcms/sdk/solana" @@ -82,19 +81,15 @@ func (s *MCMSWithTimelockProgramsSolana) SetState(contractType deployment.Contra case types.AccessControllerProgram: s.AccessControllerProgram = program case types.ProposerAccessControllerAccount: - s.AccessControllerProgram = program - s.ProposerAccessControllerAccount = solana.PublicKey(seed) + s.ProposerAccessControllerAccount = program case types.ExecutorAccessControllerAccount: - s.AccessControllerProgram = program - s.ExecutorAccessControllerAccount = solana.PublicKey(seed) + s.ExecutorAccessControllerAccount = program case types.CancellerAccessControllerAccount: - s.AccessControllerProgram = program - s.CancellerAccessControllerAccount = solana.PublicKey(seed) + s.CancellerAccessControllerAccount = program case types.BypasserAccessControllerAccount: - s.AccessControllerProgram = program - s.BypasserAccessControllerAccount = solana.PublicKey(seed) + s.BypasserAccessControllerAccount = program default: - return fmt.Errorf("unknown program type: %s", contractType) + return fmt.Errorf("unknown contract type: %s", contractType) } return nil } @@ -235,35 +230,31 @@ func MaybeLoadMCMSWithTimelockChainStateSolana(chain deployment.SolChain, addres state.AccessControllerProgram = programID case tvStr.Type == proposerAccessControllerAccount.Type && tvStr.Version.String() == proposerAccessControllerAccount.Version.String(): - programID, account, err := DecodeAddressWithAccount(address) + account, err := solana.PublicKeyFromBase58(address) if err != nil { - return nil, fmt.Errorf("unable to decode proposer access controlle address (%s): %w", address, err) + return nil, fmt.Errorf("unable to decode proposer access controller address (%s): %w", address, err) } - state.AccessControllerProgram = programID state.ProposerAccessControllerAccount = account case tvStr.Type == executorAccessControllerAccount.Type && tvStr.Version.String() == executorAccessControllerAccount.Version.String(): - programID, account, err := DecodeAddressWithAccount(address) + account, err := solana.PublicKeyFromBase58(address) if err != nil { - return nil, fmt.Errorf("unable to decode executor access controlle address (%s): %w", address, err) + return nil, fmt.Errorf("unable to decode executor access controller address (%s): %w", address, err) } - state.AccessControllerProgram = programID state.ExecutorAccessControllerAccount = account case tvStr.Type == cancellerAccessControllerAccount.Type && tvStr.Version.String() == cancellerAccessControllerAccount.Version.String(): - programID, account, err := DecodeAddressWithAccount(address) + account, err := solana.PublicKeyFromBase58(address) if err != nil { - return nil, fmt.Errorf("unable to decode canceller access controlle address (%s): %w", address, err) + return nil, fmt.Errorf("unable to decode canceller access controller address (%s): %w", address, err) } - state.AccessControllerProgram = programID state.CancellerAccessControllerAccount = account case tvStr.Type == bypasserAccessControllerAccount.Type && tvStr.Version.String() == bypasserAccessControllerAccount.Version.String(): - programID, account, err := DecodeAddressWithAccount(address) + account, err := solana.PublicKeyFromBase58(address) if err != nil { - return nil, fmt.Errorf("unable to decode bypasser access controlle address (%s): %w", address, err) + return nil, fmt.Errorf("unable to decode bypasser access controller address (%s): %w", address, err) } - state.AccessControllerProgram = programID state.BypasserAccessControllerAccount = account case tvStr.Type == mcmProgram.Type && tvStr.Version.String() == mcmProgram.Version.String(): @@ -313,26 +304,3 @@ func DecodeAddressWithSeed(address string) (solana.PublicKey, PDASeed, error) { return programID, PDASeed(seed), nil } - -func EncodeAddressWithAccount(programID, account solana.PublicKey) string { - return fmt.Sprintf("%s.%s", programID.String(), account.String()) -} - -func DecodeAddressWithAccount(address string) (solana.PublicKey, solana.PublicKey, error) { - parts := strings.Split(address, ".") - if len(parts) != 2 { - return solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("invalid address: %s", address) - } - - programID, err := solana.PublicKeyFromBase58(parts[0]) - if err != nil { - return solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("unable to parse public key from program id: %s", parts[0]) - } - - account, err := solana.PublicKeyFromBase58(parts[1]) - if err != nil { - return solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("unable to parse public key from account: %s", parts[1]) - } - - return programID, account, nil -} diff --git a/deployment/common/changeset/test_helpers.go b/deployment/common/changeset/test_helpers.go index 7b29af4cac0..d0a472ccd8c 100644 --- a/deployment/common/changeset/test_helpers.go +++ b/deployment/common/changeset/test_helpers.go @@ -5,9 +5,15 @@ import ( "testing" mapset "github.com/deckarep/golang-set/v2" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/deployment" + commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" ) type ConfiguredChangeSet interface { @@ -114,3 +120,99 @@ func ApplyChangesets(t *testing.T, e deployment.Environment, timelockContractsPe } return currentEnv, nil } + +// ApplyChangesetsV2 applies the changeset applications to the environment and returns the updated environment. +func ApplyChangesetsV2(t *testing.T, e deployment.Environment, changesetApplications []ConfiguredChangeSet) (deployment.Environment, error) { + currentEnv := e + for i, csa := range changesetApplications { + out, err := csa.Apply(currentEnv) + if err != nil { + return e, fmt.Errorf("failed to apply changeset at index %d: %w", i, err) + } + var addresses deployment.AddressBook + if out.AddressBook != nil { + addresses = out.AddressBook + err := addresses.Merge(currentEnv.ExistingAddresses) + if err != nil { + return e, fmt.Errorf("failed to merge address book: %w", err) + } + } else { + addresses = currentEnv.ExistingAddresses + } + if out.Jobs != nil { //nolint:revive,staticcheck // we want the empty block as documentation + // do nothing, as these jobs auto-accept. + } + if out.MCMSTimelockProposals != nil { + for _, prop := range out.MCMSTimelockProposals { + chains := mapset.NewSet[uint64]() + for _, op := range prop.Operations { + chains.Add(uint64(op.ChainSelector)) + } + + p := proposalutils.SignMCMSTimelockProposal(t, e, &prop) + proposalutils.ExecuteMCMSProposalV2(t, e, p) + proposalutils.ExecuteMCMSTimelockProposalV2(t, e, &prop) + } + } + if out.MCMSProposals != nil { + for _, prop := range out.MCMSProposals { + chains := mapset.NewSet[uint64]() + for _, op := range prop.Operations { + chains.Add(uint64(op.ChainSelector)) + } + + p := proposalutils.SignMCMSProposal(t, e, &prop) + proposalutils.ExecuteMCMSProposalV2(t, e, p) + } + } + currentEnv = deployment.Environment{ + Name: e.Name, + Logger: e.Logger, + ExistingAddresses: addresses, + Chains: e.Chains, + SolChains: e.SolChains, + NodeIDs: e.NodeIDs, + Offchain: e.Offchain, + OCRSecrets: e.OCRSecrets, + GetContext: e.GetContext, + } + } + return currentEnv, nil +} + +func DeployLinkTokenTest(t *testing.T, solChains int) { + lggr := logger.Test(t) + e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Chains: 1, + SolChains: solChains, + }) + chain1 := e.AllChainSelectors()[0] + config := []uint64{chain1} + var solChain1 uint64 + if solChains > 0 { + solChain1 = e.AllChainSelectorsSolana()[0] + config = append(config, solChain1) + } + + e, err := ApplyChangesets(t, e, nil, []ConfiguredChangeSet{ + Configure( + deployment.CreateLegacyChangeSet(DeployLinkToken), + config, + ), + }) + require.NoError(t, err) + addrs, err := e.ExistingAddresses.AddressesForChain(chain1) + require.NoError(t, err) + state, err := commonState.MaybeLoadLinkTokenChainState(e.Chains[chain1], addrs) + require.NoError(t, err) + // View itself already unit tested + _, err = state.GenerateLinkView() + require.NoError(t, err) + + // solana test + if solChains > 0 { + addrs, err = e.ExistingAddresses.AddressesForChain(solChain1) + require.NoError(t, err) + require.NotEmpty(t, addrs) + } +} diff --git a/deployment/common/proposalutils/mcms_helpers.go b/deployment/common/proposalutils/mcms_helpers.go index ad6b3d227ba..3cf8ecf8795 100644 --- a/deployment/common/proposalutils/mcms_helpers.go +++ b/deployment/common/proposalutils/mcms_helpers.go @@ -19,6 +19,7 @@ import ( mcmstypes "github.com/smartcontractkit/mcms/types" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/types" ) @@ -30,47 +31,6 @@ type TimelockExecutionContracts struct { CallProxy *owner_helpers.CallProxy } -// NewTimelockExecutionContracts creates a new TimelockExecutionContracts struct. -// If there are multiple timelocks or call proxy on the chain, an error is returned. -// If there is a missing timelocks or call proxy on the chain, an error is returned. -func NewTimelockExecutionContracts(env deployment.Environment, chainSelector uint64) (*TimelockExecutionContracts, error) { - addrTypeVer, err := env.ExistingAddresses.AddressesForChain(chainSelector) - if err != nil { - return nil, fmt.Errorf("error getting addresses for chain: %w", err) - } - var timelock *owner_helpers.RBACTimelock - var callProxy *owner_helpers.CallProxy - for addr, tv := range addrTypeVer { - if tv.Type == types.RBACTimelock { - if timelock != nil { - return nil, fmt.Errorf("multiple timelocks found on chain %d", chainSelector) - } - var err error - timelock, err = owner_helpers.NewRBACTimelock(common.HexToAddress(addr), env.Chains[chainSelector].Client) - if err != nil { - return nil, fmt.Errorf("error creating timelock: %w", err) - } - } - if tv.Type == types.CallProxy { - if callProxy != nil { - return nil, fmt.Errorf("multiple call proxies found on chain %d", chainSelector) - } - var err error - callProxy, err = owner_helpers.NewCallProxy(common.HexToAddress(addr), env.Chains[chainSelector].Client) - if err != nil { - return nil, fmt.Errorf("error creating call proxy: %w", err) - } - } - } - if timelock == nil || callProxy == nil { - return nil, fmt.Errorf("missing timelock (%T) or call proxy(%T) on chain %d", timelock == nil, callProxy == nil, chainSelector) - } - return &TimelockExecutionContracts{ - Timelock: timelock, - CallProxy: callProxy, - }, nil -} - type RunTimelockExecutorConfig struct { Executor *mcms.Executor TimelockContracts *TimelockExecutionContracts diff --git a/deployment/common/proposalutils/mcms_test_helpers.go b/deployment/common/proposalutils/mcms_test_helpers.go index 1340236fc30..c49dddcc301 100644 --- a/deployment/common/proposalutils/mcms_test_helpers.go +++ b/deployment/common/proposalutils/mcms_test_helpers.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "math/big" "testing" + "time" "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" @@ -13,11 +14,11 @@ import ( "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" chainsel "github.com/smartcontractkit/chain-selectors" mcmslib "github.com/smartcontractkit/mcms" - "github.com/smartcontractkit/mcms/sdk" - "github.com/smartcontractkit/mcms/sdk/evm" - "github.com/smartcontractkit/mcms/sdk/solana" - "github.com/smartcontractkit/mcms/types" + mcmssdk "github.com/smartcontractkit/mcms/sdk" + mcmsevmsdk "github.com/smartcontractkit/mcms/sdk/evm" + mcmssolanasdk "github.com/smartcontractkit/mcms/sdk/solana" mcmstypes "github.com/smartcontractkit/mcms/types" + "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/deployment" @@ -102,22 +103,21 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex // SignMCMSTimelockProposal - Signs an MCMS timelock proposal. func SignMCMSTimelockProposal(t *testing.T, env deployment.Environment, proposal *mcmslib.TimelockProposal) *mcmslib.Proposal { - converters := make(map[types.ChainSelector]sdk.TimelockConverter) - inspectorsMap := make(map[types.ChainSelector]sdk.Inspector) + converters := make(map[mcmstypes.ChainSelector]mcmssdk.TimelockConverter) + inspectorsMap := make(map[mcmstypes.ChainSelector]mcmssdk.Inspector) for _, chain := range env.Chains { _, exists := chainsel.ChainBySelector(chain.Selector) require.True(t, exists) - chainSel := types.ChainSelector(chain.Selector) - converters[chainSel] = &evm.TimelockConverter{} - inspectorsMap[chainSel] = evm.NewInspector(chain.Client) + chainSel := mcmstypes.ChainSelector(chain.Selector) + converters[chainSel] = &mcmsevmsdk.TimelockConverter{} + inspectorsMap[chainSel] = mcmsevmsdk.NewInspector(chain.Client) } - - for _, chain := range env.SolChains { - _, exists := chainsel.SolanaChainBySelector(chain.Selector) - require.True(t, exists) - chainSel := types.ChainSelector(chain.Selector) - converters[chainSel] = &solana.TimelockConverter{} - inspectorsMap[chainSel] = solana.NewInspector(chain.Client) + for chainSelector, chain := range env.SolChains { + _, err := chainsel.SolanaChainIdFromSelector(chainSelector) + require.NoError(t, err) + chainSel := mcmstypes.ChainSelector(chainSelector) + converters[chainSel] = mcmssolanasdk.NewTimelockConverter(chain.Client) + inspectorsMap[chainSel] = mcmssolanasdk.NewInspector(chain.Client) } p, _, err := proposal.Convert(env.GetContext(), converters) @@ -144,22 +144,22 @@ func SignMCMSTimelockProposal(t *testing.T, env deployment.Environment, proposal // SignMCMSProposal - Signs an MCMS proposal. For timelock proposal, use SignMCMSTimelockProposal instead. func SignMCMSProposal(t *testing.T, env deployment.Environment, proposal *mcmslib.Proposal) *mcmslib.Proposal { - converters := make(map[types.ChainSelector]sdk.TimelockConverter) - inspectorsMap := make(map[types.ChainSelector]sdk.Inspector) + converters := make(map[mcmstypes.ChainSelector]mcmssdk.TimelockConverter) + inspectorsMap := make(map[mcmstypes.ChainSelector]mcmssdk.Inspector) for _, chain := range env.Chains { chainselc, exists := chainsel.ChainBySelector(chain.Selector) require.True(t, exists) - chainSel := types.ChainSelector(chainselc.Selector) - converters[chainSel] = &evm.TimelockConverter{} - inspectorsMap[chainSel] = evm.NewInspector(chain.Client) + chainSel := mcmstypes.ChainSelector(chainselc.Selector) + converters[chainSel] = &mcmsevmsdk.TimelockConverter{} + inspectorsMap[chainSel] = mcmsevmsdk.NewInspector(chain.Client) } for _, chain := range env.SolChains { _, exists := chainsel.SolanaChainBySelector(chain.Selector) require.True(t, exists) - chainSel := types.ChainSelector(chain.Selector) - converters[chainSel] = &solana.TimelockConverter{} - inspectorsMap[chainSel] = solana.NewInspector(chain.Client) + chainSel := mcmstypes.ChainSelector(chain.Selector) + converters[chainSel] = &mcmssolanasdk.TimelockConverter{} + inspectorsMap[chainSel] = mcmssolanasdk.NewInspector(chain.Client) } proposal.UseSimulatedBackend(true) @@ -186,39 +186,47 @@ func ExecuteMCMSProposalV2(t *testing.T, env deployment.Environment, proposal *m t.Log("Executing proposal") encoders, err := proposal.GetEncoders() - require.NoError(t, err) + require.NoError(t, err, "[ExecuteMCMSProposalV2] failed to get encoders") // build a map with chainSelector => executor - executorsMap := map[types.ChainSelector]sdk.Executor{} + executorsMap := map[mcmstypes.ChainSelector]mcmssdk.Executor{} for _, op := range proposal.Operations { family, err := chainsel.GetSelectorFamily(uint64(op.ChainSelector)) require.NoError(t, err) switch family { case chainsel.FamilyEVM: - encoder := encoders[op.ChainSelector].(*evm.Encoder) - executorsMap[op.ChainSelector] = evm.NewExecutor( + encoder := encoders[op.ChainSelector].(*mcmsevmsdk.Encoder) + executorsMap[op.ChainSelector] = mcmsevmsdk.NewExecutor( encoder, env.Chains[uint64(op.ChainSelector)].Client, env.Chains[uint64(op.ChainSelector)].DeployerKey) + t.Logf("[ExecuteMCMSProposalV2] Using EVM chain with chainID=%d", uint64(op.ChainSelector)) case chainsel.FamilySolana: - encoder := encoders[op.ChainSelector].(*solana.Encoder) - executorsMap[op.ChainSelector] = solana.NewExecutor( + encoder := encoders[op.ChainSelector].(*mcmssolanasdk.Encoder) + executorsMap[op.ChainSelector] = mcmssolanasdk.NewExecutor( encoder, env.SolChains[uint64(op.ChainSelector)].Client, *env.SolChains[uint64(op.ChainSelector)].DeployerKey) + t.Logf("[ExecuteMCMSProposalV2] Using Solana chain with chainID=%d. RPC=%s. Authority=%s", + uint64(op.ChainSelector), + env.SolChains[uint64(op.ChainSelector)].URL, + env.SolChains[uint64(op.ChainSelector)].DeployerKey.PublicKey().String(), + ) + default: require.FailNow(t, "unsupported chain family") } } executable, err := mcmslib.NewExecutable(proposal, executorsMap) - require.NoError(t, err) + require.NoError(t, err, "[ExecuteMCMSProposalV2] failed to build executable") // call SetRoot for each chain for chainSelector := range executorsMap { + t.Logf("[ExecuteMCMSProposalV2] Setting root on chain %d...", chainSelector) root, err := executable.SetRoot(env.GetContext(), chainSelector) - require.NoError(t, deployment.MaybeDataErr(err)) + require.NoError(t, deployment.MaybeDataErr(err), "[ExecuteMCMSProposalV2] SetRoot failed") family, err := chainsel.GetSelectorFamily(uint64(chainSelector)) require.NoError(t, err) @@ -227,6 +235,7 @@ func ExecuteMCMSProposalV2(t *testing.T, env deployment.Environment, proposal *m if family == chainsel.FamilyEVM { chain := env.Chains[uint64(chainSelector)] evmTransaction := root.RawTransaction.(*gethtypes.Transaction) + t.Logf("[ExecuteMCMSProposalV2] SetRoot EVM tx hash: %s", evmTransaction.Hash().String()) _, err = chain.Confirm(evmTransaction) require.NoError(t, err) } @@ -234,6 +243,7 @@ func ExecuteMCMSProposalV2(t *testing.T, env deployment.Environment, proposal *m // execute each operation sequentially for i, op := range proposal.Operations { + t.Logf("[ExecuteMCMSProposalV2] Executing operation index=%d on chain %d...", i, uint64(op.ChainSelector)) result, err := executable.Execute(env.GetContext(), i) require.NoError(t, err) @@ -243,6 +253,7 @@ func ExecuteMCMSProposalV2(t *testing.T, env deployment.Environment, proposal *m if family == chainsel.FamilyEVM { chain := env.Chains[uint64(op.ChainSelector)] evmTransaction := result.RawTransaction.(*gethtypes.Transaction) + t.Logf("[ExecuteMCMSProposalV2] Operation %d EVM tx hash: %s", i, evmTransaction.Hash().String()) _, err = chain.Confirm(evmTransaction) require.NoError(t, err) } @@ -255,18 +266,18 @@ func ExecuteMCMSTimelockProposalV2(t *testing.T, env deployment.Environment, tim t.Log("Executing timelock proposal") // build a "chainSelector => executor" map - executorsMap := map[types.ChainSelector]sdk.TimelockExecutor{} + executorsMap := map[mcmstypes.ChainSelector]mcmssdk.TimelockExecutor{} for _, op := range timelockProposal.Operations { family, err := chainsel.GetSelectorFamily(uint64(op.ChainSelector)) require.NoError(t, err) switch family { case chainsel.FamilyEVM: - executorsMap[op.ChainSelector] = evm.NewTimelockExecutor( + executorsMap[op.ChainSelector] = mcmsevmsdk.NewTimelockExecutor( env.Chains[uint64(op.ChainSelector)].Client, env.Chains[uint64(op.ChainSelector)].DeployerKey) case chainsel.FamilySolana: - executorsMap[op.ChainSelector] = solana.NewTimelockExecutor( + executorsMap[op.ChainSelector] = mcmssolanasdk.NewTimelockExecutor( env.SolChains[uint64(op.ChainSelector)].Client, *env.SolChains[uint64(op.ChainSelector)].DeployerKey) default: @@ -277,11 +288,12 @@ func ExecuteMCMSTimelockProposalV2(t *testing.T, env deployment.Environment, tim timelockExecutable, err := mcmslib.NewTimelockExecutable(timelockProposal, executorsMap) require.NoError(t, err) - err = timelockExecutable.IsReady(env.GetContext()) - require.NoError(t, err) + require.Eventually(t, func() bool { + return timelockExecutable.IsReady(env.GetContext()) == nil + }, 5*time.Second, 50*time.Millisecond) // execute each operation sequentially - var tx = types.TransactionResult{} + var tx = mcmstypes.TransactionResult{} for i, op := range timelockProposal.Operations { tx, err = timelockExecutable.Execute(env.GetContext(), i, opts...) require.NoError(t, err) diff --git a/deployment/environment/memory/chain.go b/deployment/environment/memory/chain.go index 99fb3de8efc..b6fd091ec12 100644 --- a/deployment/environment/memory/chain.go +++ b/deployment/environment/memory/chain.go @@ -3,6 +3,7 @@ package memory import ( "context" "encoding/json" + "errors" "fmt" "math/big" "os" @@ -119,6 +120,49 @@ func generateSolanaKeypair(t testing.TB) (solana.PrivateKey, string, error) { return privateKey, keypairPath, nil } +func FundSolanaAccounts( + ctx context.Context, t *testing.T, accounts []solana.PublicKey, solAmount uint64, solanaGoClient *solRpc.Client, +) { + t.Helper() + + var sigs = make([]solana.Signature, 0, len(accounts)) + for _, account := range accounts { + sig, err := solanaGoClient.RequestAirdrop(ctx, account, solAmount*solana.LAMPORTS_PER_SOL, solRpc.CommitmentConfirmed) + require.NoError(t, err) + sigs = append(sigs, sig) + } + + const timeout = 10 * time.Second + const pollInterval = 50 * time.Millisecond + + timeoutCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + ticker := time.NewTicker(pollInterval) + defer ticker.Stop() + + remaining := len(sigs) + for remaining > 0 { + select { + case <-timeoutCtx.Done(): + require.NoError(t, errors.New("unable to find transaction within timeout")) + case <-ticker.C: + statusRes, sigErr := solanaGoClient.GetSignatureStatuses(ctx, true, sigs...) + require.NoError(t, sigErr) + require.NotNil(t, statusRes) + require.NotNil(t, statusRes.Value) + + unconfirmedTxCount := 0 + for _, res := range statusRes.Value { + if res == nil || res.ConfirmationStatus == solRpc.ConfirmationStatusProcessed { + unconfirmedTxCount++ + } + } + remaining = unconfirmedTxCount + } + } +} + func GenerateChainsSol(t *testing.T, numChains int) map[uint64]SolanaChain { testSolanaChainSelectors := getTestSolanaChainSelectors() if len(testSolanaChainSelectors) < numChains {