From e7725b3ba205097c737508566555a9916f4488df Mon Sep 17 00:00:00 2001 From: Joe Clapis Date: Tue, 21 May 2024 21:23:26 -0400 Subject: [PATCH 1/6] Started scaffolding pieces --- .github/ISSUE_TEMPLATE/bug_report.md | 32 ++ .github/workflows/build.yml | 19 + .github/workflows/commits.yml | 14 + .github/workflows/lint.yml | 49 ++ .github/workflows/unit-tests.yml | 19 + .gitignore | 6 + LICENSE | 661 +++++++++++++++++++++++++++ db/config.go | 1 + db/database.go | 95 ++++ db/validator.go | 12 + go.mod | 22 + go.sum | 34 ++ manager/manager.go | 39 ++ server/server.go | 52 +++ server/set-balance.go | 8 + server/set-status.go | 8 + server/slash.go | 17 + 17 files changed, 1088 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/commits.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/unit-tests.yml create mode 100644 LICENSE create mode 100644 db/config.go create mode 100644 db/database.go create mode 100644 db/validator.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 manager/manager.go create mode 100644 server/server.go create mode 100644 server/set-balance.go create mode 100644 server/set-status.go create mode 100644 server/slash.go diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..687f5d9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. [command 1] +2. [command 2] +3. etc. +4. [error output] + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Environment Info:** + - OS + version: [e.g. Debian 12, Fedora 39, Arch (btw), etc] + - Beacon client + version: [e.g. Nimbus 24.2.2, Prysm 5.0.1, etc] + - Execution client + version: [e.g. Geth 1.13.14, Reth 0.2.0, etc] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ce6664d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,19 @@ +name: Build +on: + push: + tags: + - v* + branches: + - main + pull_request: +permissions: + contents: read +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.21.10 + - run: cd ${GITHUB_WORKSPACE} && go build . diff --git a/.github/workflows/commits.yml b/.github/workflows/commits.yml new file mode 100644 index 0000000..fde759a --- /dev/null +++ b/.github/workflows/commits.yml @@ -0,0 +1,14 @@ +# Taken from https://github.com/marketplace/actions/block-fixup-commit-merge?version=v2.0.0 +# Updated to use newer ubuntu and checkout action +name: Git Checks + +on: [pull_request] + +jobs: + block-fixup: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Block Fixup Commit Merge + uses: 13rac1/block-fixup-merge-action@v2.0.0 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..1481911 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,49 @@ +# Taken from https://github.com/golangci/golangci-lint-action +name: golangci-lint +on: + push: + tags: + - v* + branches: + - main + pull_request: +permissions: + # Required: allow read access to the content for analysis. + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read + # Optional: allow write access to checks to allow the action to annotate code in the PR. + checks: write +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.21.10 + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: latest + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the all caching functionality will be complete disabled, + # takes precedence over all other caching options. + # skip-cache: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 0000000..77de9cb --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,19 @@ +name: Unit Tests +on: + push: + tags: + - v* + branches: + - main + pull_request: +permissions: + contents: read +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.21.10 + - run: go test ./... diff --git a/.gitignore b/.gitignore index 3b735ec..1cbb4b9 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ # Test binary, built with `go test -c` *.test +# VS Code +.vscode/ + # Output of the go coverage tool, specifically when used with LiteIDE *.out @@ -19,3 +22,6 @@ # Go workspace file go.work + +# Binaries +beacon-mock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/db/config.go b/db/config.go new file mode 100644 index 0000000..3a49c63 --- /dev/null +++ b/db/config.go @@ -0,0 +1 @@ +package db diff --git a/db/database.go b/db/database.go new file mode 100644 index 0000000..ac7f0d3 --- /dev/null +++ b/db/database.go @@ -0,0 +1,95 @@ +package db + +import ( + "fmt" + "log/slog" + "strconv" + + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/node-manager-core/beacon" +) + +const ( + FarFutureEpoch uint64 = 0x7fffffffffffffff +) + +type Database struct { + validators []*beacon.ValidatorStatus + validatorPubkeyMap map[beacon.ValidatorPubkey]*beacon.ValidatorStatus + + // Internal fields + logger *slog.Logger +} + +func NewDatabase(logger *slog.Logger) *Database { + return &Database{ + logger: logger, + validators: []*beacon.ValidatorStatus{}, + validatorPubkeyMap: make(map[beacon.ValidatorPubkey]*beacon.ValidatorStatus), + } +} + +func (db *Database) AddValidator(pubkey beacon.ValidatorPubkey, withdrawalCredentials common.Hash) (*beacon.ValidatorStatus, error) { + if _, exists := db.validatorPubkeyMap[pubkey]; exists { + return nil, fmt.Errorf("validator with pubkey %s already exists", pubkey.HexWithPrefix()) + } + + index := len(db.validators) + validator := &beacon.ValidatorStatus{ + Pubkey: pubkey, + Index: strconv.FormatInt(int64(index), 10), + WithdrawalCredentials: withdrawalCredentials, + Balance: 32e9, + Status: beacon.ValidatorState_PendingInitialized, + EffectiveBalance: 32e9, + Slashed: false, + ActivationEligibilityEpoch: 0, + ActivationEpoch: 0, + ExitEpoch: FarFutureEpoch, + WithdrawableEpoch: FarFutureEpoch, + Exists: true, + } + db.validators = append(db.validators, validator) + db.validatorPubkeyMap[pubkey] = validator + return validator, nil +} + +// Get a validator by its index. Returns nil if it doesn't exist. +func (db *Database) GetValidatorByIndex(index uint) *beacon.ValidatorStatus { + dbLength := len(db.validators) + if index >= uint(dbLength) { + return nil + } + + return db.validators[index] +} + +// Get a validator by its pubkey. Returns nil if it doesn't exist. +func (db *Database) GetValidatorByPubkey(pubkey beacon.ValidatorPubkey) *beacon.ValidatorStatus { + return db.validatorPubkeyMap[pubkey] +} + +func (db *Database) Clone() *Database { + clone := NewDatabase(db.logger) + cloneValidators := make([]*beacon.ValidatorStatus, len(db.validators)) + for i, validator := range db.validators { + cloneValidator := &beacon.ValidatorStatus{ + Pubkey: validator.Pubkey, + Index: validator.Index, + WithdrawalCredentials: validator.WithdrawalCredentials, + Balance: validator.Balance, + Status: validator.Status, + EffectiveBalance: validator.EffectiveBalance, + Slashed: validator.Slashed, + ActivationEligibilityEpoch: validator.ActivationEligibilityEpoch, + ActivationEpoch: validator.ActivationEpoch, + ExitEpoch: validator.ExitEpoch, + WithdrawableEpoch: validator.WithdrawableEpoch, + Exists: validator.Exists, + } + cloneValidators[i] = cloneValidator + clone.validatorPubkeyMap[validator.Pubkey] = cloneValidator + } + clone.validators = cloneValidators + return clone +} diff --git a/db/validator.go b/db/validator.go new file mode 100644 index 0000000..91f6e44 --- /dev/null +++ b/db/validator.go @@ -0,0 +1,12 @@ +package db + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/node-manager-core/beacon" +) + +type Validator struct { + Index uint64 + Pubkey beacon.ValidatorPubkey + WithdrawalCredentials common.Hash +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0080c64 --- /dev/null +++ b/go.mod @@ -0,0 +1,22 @@ +module github.com/nodeset-org/beacon-mock + +go 1.21 + +require ( + github.com/ethereum/go-ethereum v1.14.0 + github.com/gorilla/mux v1.8.1 + github.com/rocket-pool/node-manager-core v0.3.1 +) + +require ( + github.com/goccy/go-json v0.10.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/sethvargo/go-password v0.2.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/sys v0.19.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5ec7800 --- /dev/null +++ b/go.sum @@ -0,0 +1,34 @@ +github.com/ethereum/go-ethereum v1.14.0 h1:xRWC5NlB6g1x7vNy4HDBLuqVNbtLrc7v8S6+Uxim1LU= +github.com/ethereum/go-ethereum v1.14.0/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw= +github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= +github.com/rocket-pool/node-manager-core v0.3.1 h1:9M6HFH4Kgy1wYUYqrh3+A1RdSe/HjndIWMBUBhjxVk0= +github.com/rocket-pool/node-manager-core v0.3.1/go.mod h1:f/w3jnzi3ipXet7acZ0pQhGbolU/g3IDVrqXcNWuHw4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= +github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/manager/manager.go b/manager/manager.go new file mode 100644 index 0000000..6b7fe70 --- /dev/null +++ b/manager/manager.go @@ -0,0 +1,39 @@ +package manager + +import ( + "fmt" + "log/slog" + + "github.com/nodeset-org/beacon-mock/db" +) + +type BeaconMockManager struct { + Database *db.Database + + // Internal fields + snapshots map[string]*db.Database + logger *slog.Logger +} + +func NewBeaconMockManager(logger *slog.Logger) *BeaconMockManager { + return &BeaconMockManager{ + Database: db.NewDatabase(logger), + snapshots: map[string]*db.Database{}, + logger: logger, + } +} + +func (m *BeaconMockManager) TakeSnapshot(name string) { + m.snapshots[name] = m.Database.Clone() + m.logger.Info("Took DB snapshot", "name", name) +} + +func (m *BeaconMockManager) RevertToSnapshot(name string) error { + snapshot, exists := m.snapshots[name] + if !exists { + return fmt.Errorf("snapshot with name [%s] does not exist", name) + } + m.Database = snapshot + m.logger.Info("Reverted to DB snapshot", "name", name) + return nil +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..d03b15c --- /dev/null +++ b/server/server.go @@ -0,0 +1,52 @@ +package server + +import ( + "fmt" + "log/slog" + "net" + "net/http" + "strconv" + "strings" + + "github.com/gorilla/mux" + "github.com/nodeset-org/beacon-mock/manager" + "github.com/rocket-pool/node-manager-core/beacon" +) + +type BeaconMockServer struct { + logger *slog.Logger + ip string + port uint16 + socket net.Listener + server http.Server + router *mux.Router + manager *manager.BeaconMockManager +} + +// ============= +// === Utils === +// ============= + +func (s *BeaconMockServer) getValidatorByID(id string) (*beacon.ValidatorStatus, error) { + if strings.HasPrefix(id, "0x") { + pubkey, err := beacon.HexToValidatorPubkey(id) + if err != nil { + return nil, err + } + validator := s.manager.Database.GetValidatorByPubkey(pubkey) + if validator == nil { + return nil, fmt.Errorf("validator with pubkey %s does not exist", pubkey.HexWithPrefix()) + } + return validator, nil + } + + index, err := strconv.ParseUint(id, 10, 64) + if err != nil { + return nil, err + } + validator := s.manager.Database.GetValidatorByIndex(uint(index)) + if validator == nil { + return nil, fmt.Errorf("validator with index %d does not exist", index) + } + return validator, nil +} diff --git a/server/set-balance.go b/server/set-balance.go new file mode 100644 index 0000000..a29d307 --- /dev/null +++ b/server/set-balance.go @@ -0,0 +1,8 @@ +package server + +import "github.com/rocket-pool/node-manager-core/beacon" + +func (s *BeaconMockServer) SetBalance(validator *beacon.ValidatorStatus, newBalanceGwei uint64) error { + validator.Balance = newBalanceGwei + return nil +} diff --git a/server/set-status.go b/server/set-status.go new file mode 100644 index 0000000..9e5f90f --- /dev/null +++ b/server/set-status.go @@ -0,0 +1,8 @@ +package server + +import "github.com/rocket-pool/node-manager-core/beacon" + +func (s *BeaconMockServer) SetStatus(validator *beacon.ValidatorStatus, newStatus beacon.ValidatorState) error { + validator.Status = newStatus + return nil +} diff --git a/server/slash.go b/server/slash.go new file mode 100644 index 0000000..2ec5a0d --- /dev/null +++ b/server/slash.go @@ -0,0 +1,17 @@ +package server + +import ( + "fmt" + + "github.com/rocket-pool/node-manager-core/beacon" +) + +func (s *BeaconMockServer) SlashValidator(validator *beacon.ValidatorStatus, penaltyGwei uint64) error { + if validator.Status != beacon.ValidatorState_ActiveOngoing && validator.Status != beacon.ValidatorState_ActiveExiting { + return fmt.Errorf("validator with pubkey %s is not in a slashable state", validator.Pubkey.HexWithPrefix()) + } + validator.Slashed = true + validator.Balance -= penaltyGwei + validator.Status = beacon.ValidatorState_ActiveSlashed + return nil +} From b0197038c0012de6e3bc82ce636bb988d4aa0dcf Mon Sep 17 00:00:00 2001 From: Joe Clapis Date: Wed, 22 May 2024 17:21:18 -0400 Subject: [PATCH 2/6] Finished scaffolding, started writing API route tests --- api/requests.go | 5 + api/responses.go | 18 ++ api/routes.go | 18 ++ db/cfg_test.go | 20 +++ db/config.go | 96 +++++++++++ db/database.go | 89 +++++----- db/settings.go | 6 + db/validator.go | 74 ++++++++- db/validator_test.go | 27 +++ go.mod | 70 +++++++- go.sum | 291 ++++++++++++++++++++++++++++++++- internal/db/database_test.go | 20 +++ internal/db/provision.go | 54 ++++++ internal/test/utils.go | 9 + manager/manager.go | 93 ++++++++++- server/add-validator.go | 47 ++++++ server/get-sync-status.go | 24 +++ server/get-sync-status_test.go | 69 ++++++++ server/get-validator.go | 44 +++++ server/get-validators.go | 99 +++++++++++ server/response-handling.go | 88 ++++++++++ server/server.go | 169 ++++++++++++++++--- server/server_test.go | 108 ++++++++++++ server/set-balance.go | 40 ++++- server/set-slot.go | 28 ++++ server/set-status.go | 42 ++++- server/slash.go | 45 ++++- server/utils.go | 25 +++ 28 files changed, 1624 insertions(+), 94 deletions(-) create mode 100644 api/requests.go create mode 100644 api/responses.go create mode 100644 api/routes.go create mode 100644 db/cfg_test.go create mode 100644 db/settings.go create mode 100644 db/validator_test.go create mode 100644 internal/db/database_test.go create mode 100644 internal/db/provision.go create mode 100644 internal/test/utils.go create mode 100644 server/add-validator.go create mode 100644 server/get-sync-status.go create mode 100644 server/get-sync-status_test.go create mode 100644 server/get-validator.go create mode 100644 server/get-validators.go create mode 100644 server/response-handling.go create mode 100644 server/server_test.go create mode 100644 server/set-slot.go create mode 100644 server/utils.go diff --git a/api/requests.go b/api/requests.go new file mode 100644 index 0000000..47d1e8e --- /dev/null +++ b/api/requests.go @@ -0,0 +1,5 @@ +package api + +type ValidatorsRequest struct { + IDs []string `json:"ids"` +} diff --git a/api/responses.go b/api/responses.go new file mode 100644 index 0000000..0205564 --- /dev/null +++ b/api/responses.go @@ -0,0 +1,18 @@ +package api + +import ( + "github.com/rocket-pool/node-manager-core/beacon/client" +) + +type ErrorResponse struct { + Code int `json:"code"` + Message string `json:"message"` +} + +type ValidatorResponse struct { + Data client.Validator `json:"data"` +} + +type AddValidatorResponse struct { + Index uint64 `json:"index"` +} diff --git a/api/routes.go b/api/routes.go new file mode 100644 index 0000000..49f7600 --- /dev/null +++ b/api/routes.go @@ -0,0 +1,18 @@ +package api + +const ( + StateID string = "state_id" + ValidatorID string = "validator_id" + + // Beacon API routes + ValidatorsRoute string = "v1/beacon/states/{" + StateID + "}/validators" + ValidatorRoute string = "v1/beacon/states/{" + StateID + "}/validators/{" + ValidatorID + "}" + SyncingRoute string = "v1/node/syncing" + + // Admin routes + AddValidatorRoute string = "add-validator" + SetBalanceRoute string = "set-balance" + SetStatusRoute string = "set-status" + SetSlotRoute string = "set-slot" + SlashRoute string = "slash" +) diff --git a/db/cfg_test.go b/db/cfg_test.go new file mode 100644 index 0000000..a7715ff --- /dev/null +++ b/db/cfg_test.go @@ -0,0 +1,20 @@ +package db + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/beacon-mock/internal/test" + "github.com/stretchr/testify/require" +) + +func TestConfigClone(t *testing.T) { + depositContract := common.HexToAddress(test.DepositContractAddressString) + c := NewConfig(depositContract, true) + clone := c.Clone() + t.Log("Created config and clone") + + require.NotSame(t, c, clone) + require.Equal(t, c, clone) + t.Log("Configs are equal") +} diff --git a/db/config.go b/db/config.go index 3a49c63..6e4e919 100644 --- a/db/config.go +++ b/db/config.go @@ -1 +1,97 @@ package db + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + DefaultChainID uint64 = 0x90de5e7 +) + +var ( + // Default config + defaultConfig *Config = &Config{ + ChainID: DefaultChainID, + SecondsPerSlot: 12, + SlotsPerEpoch: 32, + EpochsPerSyncCommitteePeriod: 256, + GenesisTime: time.Now(), + GenesisForkVersion: []byte{0x00}, + GenesisValidatorsRoot: []byte{0x00}, + AltairForkVersion: common.FromHex("0x90de5e700"), + AltairForkEpoch: 0, + BellatrixForkVersion: common.FromHex("0x90de5e701"), + BellatrixForkEpoch: 0, + CapellaForkVersion: common.FromHex("0x90de5e702"), + CapellaForkEpoch: 0, + DenebForkVersion: common.FromHex("0x90de5e703"), + DenebForkEpoch: 0, + } +) + +// Basic Beacon Chain configuration +type Config struct { + // Basic settings + ChainID uint64 + SecondsPerSlot uint64 + SlotsPerEpoch uint64 + EpochsPerSyncCommitteePeriod uint64 + DepositContract common.Address + + // Genesis info + GenesisTime time.Time + GenesisForkVersion []byte + GenesisValidatorsRoot []byte + + // Altair info + AltairForkVersion []byte + AltairForkEpoch uint64 + + // Bellatrix info + BellatrixForkVersion []byte + BellatrixForkEpoch uint64 + + // Capella info + CapellaForkVersion []byte + CapellaForkEpoch uint64 + + // Deneb info + DenebForkVersion []byte + DenebForkEpoch uint64 +} + +// Creates a new config instance +func NewConfig(depositContract common.Address, useDefaults bool) *Config { + config := &Config{} + if !useDefaults { + return config + } + + config = defaultConfig.Clone() + config.DepositContract = depositContract + return config +} + +// Clones a config into a new instance +func (c *Config) Clone() *Config { + return &Config{ + ChainID: c.ChainID, + SecondsPerSlot: c.SecondsPerSlot, + SlotsPerEpoch: c.SlotsPerEpoch, + EpochsPerSyncCommitteePeriod: c.EpochsPerSyncCommitteePeriod, + DepositContract: c.DepositContract, + GenesisTime: c.GenesisTime, + GenesisForkVersion: c.GenesisForkVersion, + GenesisValidatorsRoot: c.GenesisValidatorsRoot, + AltairForkVersion: c.AltairForkVersion, + AltairForkEpoch: c.AltairForkEpoch, + BellatrixForkVersion: c.BellatrixForkVersion, + BellatrixForkEpoch: c.BellatrixForkEpoch, + CapellaForkVersion: c.CapellaForkVersion, + CapellaForkEpoch: c.CapellaForkEpoch, + DenebForkVersion: c.DenebForkVersion, + DenebForkEpoch: c.DenebForkEpoch, + } +} diff --git a/db/database.go b/db/database.go index ac7f0d3..6e16f06 100644 --- a/db/database.go +++ b/db/database.go @@ -3,59 +3,53 @@ package db import ( "fmt" "log/slog" - "strconv" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/node-manager-core/beacon" ) -const ( - FarFutureEpoch uint64 = 0x7fffffffffffffff -) - +// Beacon mock database type Database struct { - validators []*beacon.ValidatorStatus - validatorPubkeyMap map[beacon.ValidatorPubkey]*beacon.ValidatorStatus + // Validators registered with the network + validators []*Validator + + // Lookup of validators by pubkey + validatorPubkeyMap map[beacon.ValidatorPubkey]*Validator + + // Current slot + currentSlot uint64 + + // Highest slot + highestSlot uint64 // Internal fields logger *slog.Logger } +// Create a new database instance func NewDatabase(logger *slog.Logger) *Database { return &Database{ logger: logger, - validators: []*beacon.ValidatorStatus{}, - validatorPubkeyMap: make(map[beacon.ValidatorPubkey]*beacon.ValidatorStatus), + validators: []*Validator{}, + validatorPubkeyMap: make(map[beacon.ValidatorPubkey]*Validator), } } -func (db *Database) AddValidator(pubkey beacon.ValidatorPubkey, withdrawalCredentials common.Hash) (*beacon.ValidatorStatus, error) { +// Add a new validator to the database. Returns an error if the validator already exists. +func (db *Database) AddValidator(pubkey beacon.ValidatorPubkey, withdrawalCredentials common.Hash) (*Validator, error) { if _, exists := db.validatorPubkeyMap[pubkey]; exists { return nil, fmt.Errorf("validator with pubkey %s already exists", pubkey.HexWithPrefix()) } index := len(db.validators) - validator := &beacon.ValidatorStatus{ - Pubkey: pubkey, - Index: strconv.FormatInt(int64(index), 10), - WithdrawalCredentials: withdrawalCredentials, - Balance: 32e9, - Status: beacon.ValidatorState_PendingInitialized, - EffectiveBalance: 32e9, - Slashed: false, - ActivationEligibilityEpoch: 0, - ActivationEpoch: 0, - ExitEpoch: FarFutureEpoch, - WithdrawableEpoch: FarFutureEpoch, - Exists: true, - } + validator := NewValidator(pubkey, withdrawalCredentials, uint64(index)) db.validators = append(db.validators, validator) db.validatorPubkeyMap[pubkey] = validator return validator, nil } // Get a validator by its index. Returns nil if it doesn't exist. -func (db *Database) GetValidatorByIndex(index uint) *beacon.ValidatorStatus { +func (db *Database) GetValidatorByIndex(index uint) *Validator { dbLength := len(db.validators) if index >= uint(dbLength) { return nil @@ -65,28 +59,41 @@ func (db *Database) GetValidatorByIndex(index uint) *beacon.ValidatorStatus { } // Get a validator by its pubkey. Returns nil if it doesn't exist. -func (db *Database) GetValidatorByPubkey(pubkey beacon.ValidatorPubkey) *beacon.ValidatorStatus { +func (db *Database) GetValidatorByPubkey(pubkey beacon.ValidatorPubkey) *Validator { return db.validatorPubkeyMap[pubkey] } +// Get all validators +func (db *Database) GetAllValidators() []*Validator { + return db.validators +} + +// Get the latest slot +func (db *Database) GetCurrentSlot() uint64 { + return db.currentSlot +} + +// Get the highest slot on the chain +func (db *Database) GetHighestSlot() uint64 { + return db.highestSlot +} + +// Set the current slot - this will also update the highest slot if the new slot is higher +func (db *Database) SetCurrentSlot(slot uint64) { + db.currentSlot = slot + if slot > db.highestSlot { + db.highestSlot = slot + } +} + +// Clone the database into a new instance func (db *Database) Clone() *Database { clone := NewDatabase(db.logger) - cloneValidators := make([]*beacon.ValidatorStatus, len(db.validators)) + clone.currentSlot = db.currentSlot + clone.highestSlot = db.highestSlot + cloneValidators := make([]*Validator, len(db.validators)) for i, validator := range db.validators { - cloneValidator := &beacon.ValidatorStatus{ - Pubkey: validator.Pubkey, - Index: validator.Index, - WithdrawalCredentials: validator.WithdrawalCredentials, - Balance: validator.Balance, - Status: validator.Status, - EffectiveBalance: validator.EffectiveBalance, - Slashed: validator.Slashed, - ActivationEligibilityEpoch: validator.ActivationEligibilityEpoch, - ActivationEpoch: validator.ActivationEpoch, - ExitEpoch: validator.ExitEpoch, - WithdrawableEpoch: validator.WithdrawableEpoch, - Exists: validator.Exists, - } + cloneValidator := validator.Clone() cloneValidators[i] = cloneValidator clone.validatorPubkeyMap[validator.Pubkey] = cloneValidator } diff --git a/db/settings.go b/db/settings.go new file mode 100644 index 0000000..9df37c3 --- /dev/null +++ b/db/settings.go @@ -0,0 +1,6 @@ +package db + +const ( + FarFutureEpoch uint64 = 0xffffffffffffffff + StartingBalance uint64 = 32e9 +) diff --git a/db/validator.go b/db/validator.go index 91f6e44..d9b3d19 100644 --- a/db/validator.go +++ b/db/validator.go @@ -1,12 +1,80 @@ package db import ( + "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/node-manager-core/beacon" ) type Validator struct { - Index uint64 - Pubkey beacon.ValidatorPubkey - WithdrawalCredentials common.Hash + Pubkey beacon.ValidatorPubkey + Index uint64 + WithdrawalCredentials common.Hash + Balance uint64 + Status beacon.ValidatorState + EffectiveBalance uint64 + Slashed bool + ActivationEligibilityEpoch uint64 + ActivationEpoch uint64 + ExitEpoch uint64 + WithdrawableEpoch uint64 +} + +func NewValidator(pubkey beacon.ValidatorPubkey, withdrawalCredentials common.Hash, index uint64) *Validator { + return &Validator{ + Pubkey: pubkey, + Index: index, + WithdrawalCredentials: withdrawalCredentials, + Balance: StartingBalance, + Status: beacon.ValidatorState_PendingInitialized, + EffectiveBalance: StartingBalance, + Slashed: false, + ActivationEligibilityEpoch: FarFutureEpoch, + ActivationEpoch: FarFutureEpoch, + ExitEpoch: FarFutureEpoch, + WithdrawableEpoch: FarFutureEpoch, + } +} + +func (v *Validator) SetBalance(balanceGwei uint64) { + v.Balance = balanceGwei + + // Rules taken from the spec: https://github.com/ethereum/annotated-spec/blob/master/phase0/beacon-chain.md#misc + if balanceGwei < v.EffectiveBalance-25e7 { + v.EffectiveBalance-- + } + if balanceGwei > v.EffectiveBalance+125e7 { + v.EffectiveBalance++ + } +} + +func (v *Validator) SetStatus(status beacon.ValidatorState) { + v.Status = status +} + +func (v *Validator) Slash(penaltyGwei uint64) error { + if v.Status != beacon.ValidatorState_ActiveOngoing && v.Status != beacon.ValidatorState_ActiveExiting { + return fmt.Errorf("validator with pubkey %s is not in a slashable state", v.Pubkey.HexWithPrefix()) + } + v.Slashed = true + v.SetBalance(v.Balance - penaltyGwei) + v.Status = beacon.ValidatorState_ActiveSlashed + return nil +} + +func (v *Validator) Clone() *Validator { + return &Validator{ + Pubkey: v.Pubkey, + Index: v.Index, + WithdrawalCredentials: v.WithdrawalCredentials, + Balance: v.Balance, + Status: v.Status, + EffectiveBalance: v.EffectiveBalance, + Slashed: v.Slashed, + ActivationEligibilityEpoch: v.ActivationEligibilityEpoch, + ActivationEpoch: v.ActivationEpoch, + ExitEpoch: v.ExitEpoch, + WithdrawableEpoch: v.WithdrawableEpoch, + } } diff --git a/db/validator_test.go b/db/validator_test.go new file mode 100644 index 0000000..be9bab9 --- /dev/null +++ b/db/validator_test.go @@ -0,0 +1,27 @@ +package db + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/beacon-mock/internal/test" + "github.com/rocket-pool/node-manager-core/beacon" + "github.com/rocket-pool/node-manager-core/node/validator" + "github.com/stretchr/testify/require" +) + +func TestValidatorClone(t *testing.T) { + pubkey, err := beacon.HexToValidatorPubkey(test.Pubkey0String) + if err != nil { + t.Fatalf("Error parsing pubkey: %v", err) + } + withdrawalCredsAddress := common.HexToAddress(test.WithdrawalCredentialsString) + withdrawalCreds := validator.GetWithdrawalCredsFromAddress(withdrawalCredsAddress) + v := NewValidator(pubkey, withdrawalCreds, 2) + clone := v.Clone() + t.Log("Created validator and clone") + + require.NotSame(t, v, clone) + require.Equal(t, v, clone) + t.Log("Validators are equal") +} diff --git a/go.mod b/go.mod index 0080c64..6d0dece 100644 --- a/go.mod +++ b/go.mod @@ -3,20 +3,80 @@ module github.com/nodeset-org/beacon-mock go 1.21 require ( - github.com/ethereum/go-ethereum v1.14.0 + github.com/ethereum/go-ethereum v1.14.3 + github.com/goccy/go-json v0.10.2 github.com/gorilla/mux v1.8.1 - github.com/rocket-pool/node-manager-core v0.3.1 + github.com/rocket-pool/node-manager-core v0.3.1-0.20240522174819-feb0ab2cc75f + github.com/stretchr/testify v1.8.4 ) require ( - github.com/goccy/go-json v0.10.2 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/ethereum/c-kzg-4844 v1.0.1 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/ferranbt/fastssz v0.1.3 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect + github.com/herumi/bls-eth-go-binary v1.33.0 // indirect github.com/holiman/uint256 v1.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.51.1 // indirect + github.com/prometheus/procfs v0.13.0 // indirect + github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44 // indirect github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/prysmaticlabs/gohashtree v0.0.4-beta // indirect + github.com/prysmaticlabs/prysm/v5 v5.0.3 // indirect github.com/sethvargo/go-password v0.2.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + github.com/wealdtech/go-bytesutil v1.2.1 // indirect + github.com/wealdtech/go-eth2-types/v2 v2.8.2 // indirect + github.com/wealdtech/go-eth2-util v1.8.2 // indirect + github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/crypto v0.22.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.19.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.20.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect + google.golang.org/grpc v1.62.1 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 5ec7800..04ac5a7 100644 --- a/go.sum +++ b/go.sum @@ -1,34 +1,319 @@ +contrib.go.opencensus.io/exporter/jaeger v0.2.1 h1:yGBYzYMewVL0yO9qqJv3Z5+IRhPdU7e9o/2oKpX4YvI= +contrib.go.opencensus.io/exporter/jaeger v0.2.1/go.mod h1:Y8IsLgdxqh1QxYxPC5IgXVmBaeLUeQFfBeBi9PbeZd0= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/bazelbuild/rules_go v0.23.2 h1:Wxu7JjqnF78cKZbsBsARLSXx/jlGaSLCnUV3mTlyHvM= +github.com/bazelbuild/rules_go v0.23.2/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/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/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= +github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= +github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/ethereum/c-kzg-4844 v1.0.1 h1:pGixCbGizcVKSwoV70ge48+PrbB+iSKs2rjgfE4yJmQ= +github.com/ethereum/c-kzg-4844 v1.0.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.14.0 h1:xRWC5NlB6g1x7vNy4HDBLuqVNbtLrc7v8S6+Uxim1LU= github.com/ethereum/go-ethereum v1.14.0/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= +github.com/ethereum/go-ethereum v1.14.3 h1:5zvnAqLtnCZrU9uod1JCvHWJbPMURzYFHfc2eHz4PHA= +github.com/ethereum/go-ethereum v1.14.3/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo= +github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= +github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= +github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= +github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/herumi/bls-eth-go-binary v1.33.0 h1:fHoysK+WbL/FQIJoVGECGd2lBLa2De7YjAGZljI2vzQ= +github.com/herumi/bls-eth-go-binary v1.33.0/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw= +github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= +github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44 h1:c3p3UzV4vFA7xaCDphnDWOjpxcadrQ26l5b+ypsvyxo= +github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44/go.mod h1:MA5zShstUwCQaE9faGHgCGvEWUbG87p4SAXINhmCkvg= github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw= github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= +github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= +github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs= +github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294 h1:q9wE0ZZRdTUAAeyFP/w0SwBEnCqlVy2+on6X2/e+eAU= +github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294/go.mod h1:ZVEbRdnMkGhp/pu35zq4SXxtvUwWK0J1MATtekZpH2Y= +github.com/prysmaticlabs/prysm/v5 v5.0.3 h1:hUi0gu6v7aXmMQkl2GbrLoWcMhDNIbkVxRwrZchKbxU= +github.com/prysmaticlabs/prysm/v5 v5.0.3/go.mod h1:v5Oz4A4cWljfxUmW7SDk/VBzoYnei+lzwJogvSqUZVs= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rocket-pool/node-manager-core v0.3.1-0.20240522174819-feb0ab2cc75f h1:wyVBEQwHcfhi4xU6DS6oeq4y+vUmxp3/+JdYSWLi9cQ= +github.com/rocket-pool/node-manager-core v0.3.1-0.20240522174819-feb0ab2cc75f/go.mod h1:Clii5aca9PvR4HoAlUs8dh2OsJbDDnJ4yL5EaQE1gSo= github.com/rocket-pool/node-manager-core v0.3.1 h1:9M6HFH4Kgy1wYUYqrh3+A1RdSe/HjndIWMBUBhjxVk0= github.com/rocket-pool/node-manager-core v0.3.1/go.mod h1:f/w3jnzi3ipXet7acZ0pQhGbolU/g3IDVrqXcNWuHw4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo= +github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8= +github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= +github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10 h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg= +github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10/go.mod h1:x/Pa0FF5Te9kdrlZKJK82YmAkvL8+f989USgz6Jiw7M= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/wealdtech/go-bytesutil v1.2.1 h1:TjuRzcG5KaPwaR5JB7L/OgJqMQWvlrblA1n0GfcXFSY= +github.com/wealdtech/go-bytesutil v1.2.1/go.mod h1:RhUDUGT1F4UP4ydqbYp2MWJbAel3M+mKd057Pad7oag= +github.com/wealdtech/go-eth2-types/v2 v2.8.2 h1:b5aXlNBLKgjAg/Fft9VvGlqAUCQMP5LzYhlHRrr4yPg= +github.com/wealdtech/go-eth2-types/v2 v2.8.2/go.mod h1:IAz9Lz1NVTaHabQa+4zjk2QDKMv8LVYo0n46M9o/TXw= +github.com/wealdtech/go-eth2-util v1.8.2 h1:gq+JMrnadifyKadUr75wmfP7+usiqMu9t3VVoob5Dvo= +github.com/wealdtech/go-eth2-util v1.8.2/go.mod h1:/80GAK0K/3+PqUBZHvaOPd3b1sjHeimxQh1nrJzgaPk= +github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1 h1:9j7bpwjT9wmwBb54ZkBhTm1uNIlFFcCJXefd/YskZPw= +github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1/go.mod h1:+tI1VD76E1WINI+Nstg7RVGpUolL5ql10nu2YztMO/4= +github.com/wealdtech/go-eth2-wallet-types/v2 v2.11.0 h1:yX9+FfUXvPDvZ8Q5bhF+64AWrQwh4a3/HpfTx99DnZc= +github.com/wealdtech/go-eth2-wallet-types/v2 v2.11.0/go.mod h1:UVP9YFcnPiIzHqbmCMW3qrQ3TK5FOqr1fmKqNT9JGr8= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa h1:Jt1XW5PaLXF1/ePZrznsh/aAUvI7Adfc3LY1dAKlzRs= +google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:K4kfzHtI0kqWA79gecJarFtDn/Mls+GxQcg3Zox91Ac= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1:RBgMaUMP+6soRkik4VoN8ojR2nex2TqZwjSSogic+eo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/apimachinery v0.20.0 h1:jjzbTJRXk0unNS71L7h3lxGDH/2HPxMPaQY+MjECKL8= +k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/client-go v0.20.0 h1:Xlax8PKbZsjX4gFvNtt4F5MoJ1V5prDvCuoq9B7iax0= +k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= +k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= +k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/internal/db/database_test.go b/internal/db/database_test.go new file mode 100644 index 0000000..2801f6c --- /dev/null +++ b/internal/db/database_test.go @@ -0,0 +1,20 @@ +package db + +import ( + "log/slog" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDatabaseClone(t *testing.T) { + // Prep the pubkeys and creds + logger := slog.Default() + d := ProvisionDatabaseForTesting(t, logger) + + // Clone the database + clone := d.Clone() + require.NotSame(t, d, clone) + require.Equal(t, d, clone) + t.Log("Databases are equal") +} diff --git a/internal/db/provision.go b/internal/db/provision.go new file mode 100644 index 0000000..2470694 --- /dev/null +++ b/internal/db/provision.go @@ -0,0 +1,54 @@ +package db + +import ( + "log/slog" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/beacon-mock/db" + "github.com/nodeset-org/beacon-mock/internal/test" + "github.com/rocket-pool/node-manager-core/beacon" + "github.com/rocket-pool/node-manager-core/node/validator" + "github.com/stretchr/testify/require" +) + +func ProvisionDatabaseForTesting(t *testing.T, logger *slog.Logger) *db.Database { + // Prep the pubkeys and creds + pubkey0, err := beacon.HexToValidatorPubkey(test.Pubkey0String) + if err != nil { + t.Fatalf("Error parsing pubkey [%s]: %v", test.Pubkey0String, err) + } + pubkey1, err := beacon.HexToValidatorPubkey(test.Pubkey1String) + if err != nil { + t.Fatalf("Error parsing pubkey [%s]: %v", test.Pubkey1String, err) + } + pubkey2, err := beacon.HexToValidatorPubkey(test.Pubkey2String) + if err != nil { + t.Fatalf("Error parsing pubkey [%s]: %v", test.Pubkey2String, err) + } + withdrawalCredsAddress := common.HexToAddress(test.WithdrawalCredentialsString) + withdrawalCreds := validator.GetWithdrawalCredsFromAddress(withdrawalCredsAddress) + t.Log("Prepped pubkeys and creds") + + // Create a new database + d := db.NewDatabase(logger) + v0, err := d.AddValidator(pubkey0, withdrawalCreds) + if err != nil { + t.Fatalf("Error adding validator [%s]: %v", pubkey0.HexWithPrefix(), err) + } + v1, err := d.AddValidator(pubkey1, withdrawalCreds) + if err != nil { + t.Fatalf("Error adding validator [%s]: %v", pubkey1.HexWithPrefix(), err) + } + v2, err := d.AddValidator(pubkey2, withdrawalCreds) + if err != nil { + t.Fatalf("Error adding validator [%s]: %v", pubkey1.HexWithPrefix(), err) + } + require.Same(t, v0, d.GetValidatorByIndex(0)) + require.Same(t, v1, d.GetValidatorByIndex(1)) + require.Same(t, v2, d.GetValidatorByIndex(2)) + require.NotSame(t, v0, v1) + require.NotSame(t, v1, v2) + t.Log("Added validators to database") + return d +} diff --git a/internal/test/utils.go b/internal/test/utils.go new file mode 100644 index 0000000..0bf6444 --- /dev/null +++ b/internal/test/utils.go @@ -0,0 +1,9 @@ +package test + +const ( + Pubkey0String string = "0xbeac0900bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + Pubkey1String string = "0xbeac0901bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + Pubkey2String string = "0xbeac0902bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + WithdrawalCredentialsString string = "0xc12ed5eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + DepositContractAddressString string = "0xde905175eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" +) diff --git a/manager/manager.go b/manager/manager.go index 6b7fe70..d53f9b2 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -3,37 +3,120 @@ package manager import ( "fmt" "log/slog" + "strconv" + "strings" + "github.com/ethereum/go-ethereum/common" "github.com/nodeset-org/beacon-mock/db" + "github.com/rocket-pool/node-manager-core/beacon" ) +// Beacon mock manager type BeaconMockManager struct { - Database *db.Database + database *db.Database + config *db.Config // Internal fields snapshots map[string]*db.Database logger *slog.Logger } -func NewBeaconMockManager(logger *slog.Logger) *BeaconMockManager { +// Create a new beacon mock manager instance +func NewBeaconMockManager(logger *slog.Logger, config *db.Config) *BeaconMockManager { return &BeaconMockManager{ - Database: db.NewDatabase(logger), + database: db.NewDatabase(logger), + config: config, snapshots: map[string]*db.Database{}, logger: logger, } } +// Create a new beacon mock manager instance using the default config +func NewBeaconMockManagerWithDefaultConfig(logger *slog.Logger, depositContract common.Address) *BeaconMockManager { + return NewBeaconMockManager(logger, db.NewConfig(depositContract, true)) +} + +// Set the database for the manager directly if you need to custom provision it +func (m *BeaconMockManager) SetDatabase(db *db.Database) { + m.database = db +} + +// Take a snapshot of the current database state func (m *BeaconMockManager) TakeSnapshot(name string) { - m.snapshots[name] = m.Database.Clone() + m.snapshots[name] = m.database.Clone() m.logger.Info("Took DB snapshot", "name", name) } +// Revert to a snapshot of the database state func (m *BeaconMockManager) RevertToSnapshot(name string) error { snapshot, exists := m.snapshots[name] if !exists { return fmt.Errorf("snapshot with name [%s] does not exist", name) } - m.Database = snapshot + m.database = snapshot m.logger.Info("Reverted to DB snapshot", "name", name) return nil } + +// Returns the manager's Beacon config +func (m *BeaconMockManager) GetConfig() *db.Config { + return m.config +} + +// Increments the Beacon chain slot - use this to have parity with mining a new block on the EL +func (m *BeaconMockManager) IncrementSlot() { + m.database.SetCurrentSlot(m.database.GetCurrentSlot() + 1) +} + +// Returns the current Beacon chain slot +func (m *BeaconMockManager) GetCurrentSlot() uint64 { + return m.database.GetCurrentSlot() +} + +// Returns the highest Beacon chain slot (top of the chain head) +func (m *BeaconMockManager) GetHighestSlot() uint64 { + return m.database.GetHighestSlot() +} + +// Sets the current Beacon chain slot +func (m *BeaconMockManager) SetCurrentSlot(slot uint64) { + m.database.SetCurrentSlot(slot) +} + +// Add a validator to the Beacon chain +func (m *BeaconMockManager) AddValidator(pubkey beacon.ValidatorPubkey, withdrawalCredentials common.Hash) (*db.Validator, error) { + return m.database.AddValidator(pubkey, withdrawalCredentials) +} + +// Gets a validator by its index or pubkey +func (m *BeaconMockManager) GetValidator(id string) (*db.Validator, error) { + if len(id) == beacon.ValidatorPubkeyLength*2 || strings.HasPrefix(id, "0x") { + pubkey, err := beacon.HexToValidatorPubkey(id) + if err != nil { + return nil, fmt.Errorf("error parsing pubkey [%s]: %v", id, err) + } + return m.database.GetValidatorByPubkey(pubkey), nil + } + index, err := strconv.ParseUint(id, 10, 32) + if err != nil { + return nil, fmt.Errorf("error parsing index [%s]: %v", id, err) + } + return m.database.GetValidatorByIndex(uint(index)), nil +} + +// Gets multiple validators by their indices or pubkeys +func (m *BeaconMockManager) GetValidators(ids []string) ([]*db.Validator, error) { + if len(ids) == 0 { + return m.database.GetAllValidators(), nil + } + + validators := []*db.Validator{} + for _, id := range ids { + validator, err := m.GetValidator(id) + if err != nil { + return nil, err + } + validators = append(validators, validator) + } + return validators, nil +} diff --git a/server/add-validator.go b/server/add-validator.go new file mode 100644 index 0000000..ccdd247 --- /dev/null +++ b/server/add-validator.go @@ -0,0 +1,47 @@ +package server + +import ( + "fmt" + "net/http" + + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/beacon-mock/api" + "github.com/rocket-pool/node-manager-core/beacon" +) + +// Handle an add validator request +func (s *BeaconMockServer) addValidator(w http.ResponseWriter, r *http.Request) { + // Get the request vars + args := s.processApiRequest(w, r, nil) + pubkeyString, exists := args["pubkey"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing pubkey")) + return + } + withdrawalCredsString, exists := args["creds"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing withdrawal creds")) + return + } + + // Input validation + pubkey, err := beacon.HexToValidatorPubkey(pubkeyString[0]) + if err != nil { + handleInputError(s.logger, w, fmt.Errorf("invalid pubkey [%s]: %w", pubkeyString[0], err)) + return + } + withdrawalCreds := common.HexToHash(withdrawalCredsString[0]) + + // Get the validator + validator, err := s.manager.AddValidator(pubkey, withdrawalCreds) + if err != nil { + handleInputError(s.logger, w, err) + return + } + + // Respond + response := api.AddValidatorResponse{ + Index: validator.Index, + } + handleSuccess(w, s.logger, response) +} diff --git a/server/get-sync-status.go b/server/get-sync-status.go new file mode 100644 index 0000000..02b6559 --- /dev/null +++ b/server/get-sync-status.go @@ -0,0 +1,24 @@ +package server + +import ( + "net/http" + + "github.com/rocket-pool/node-manager-core/beacon/client" +) + +// Handle a get sync status request +func (s *BeaconMockServer) getSyncStatus(w http.ResponseWriter, r *http.Request) { + // Get the request vars + _ = s.processApiRequest(w, r, nil) + + // Get the slots + currentSlot := s.manager.GetCurrentSlot() + highestSlot := s.manager.GetHighestSlot() + + // Write the response + response := client.SyncStatusResponse{} + response.Data.IsSyncing = (currentSlot < highestSlot) + response.Data.HeadSlot = client.Uinteger(highestSlot) + response.Data.SyncDistance = client.Uinteger(highestSlot - currentSlot) + handleSuccess(w, s.logger, response) +} diff --git a/server/get-sync-status_test.go b/server/get-sync-status_test.go new file mode 100644 index 0000000..b4233ab --- /dev/null +++ b/server/get-sync-status_test.go @@ -0,0 +1,69 @@ +package server + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "testing" + + "github.com/nodeset-org/beacon-mock/api" + idb "github.com/nodeset-org/beacon-mock/internal/db" + "github.com/rocket-pool/node-manager-core/beacon/client" + "github.com/stretchr/testify/require" +) + +// Make sure sync status requests work +func TestSyncStatus(t *testing.T) { + currentSlot := uint64(12) + + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + server.manager.SetCurrentSlot(currentSlot) + + // Create the request + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/eth/%s", port, api.SyncingRoute), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") + + // Read the body + defer response.Body.Close() + bytes, err := io.ReadAll(response.Body) + if err != nil { + t.Fatalf("error reading the response body: %v", err) + } + var parsedResponse client.SyncStatusResponse + err = json.Unmarshal(bytes, &parsedResponse) + if err != nil { + t.Fatalf("error deserializing response: %v", err) + } + + // Make sure the response is correct + require.Equal(t, currentSlot, uint64(parsedResponse.Data.HeadSlot)) + require.Equal(t, uint64(0), uint64(parsedResponse.Data.SyncDistance)) + require.False(t, parsedResponse.Data.IsSyncing) + t.Logf("Received correct response - head slot: %d, sync distance: %d, is syncing: %t", parsedResponse.Data.HeadSlot, parsedResponse.Data.SyncDistance, parsedResponse.Data.IsSyncing) +} diff --git a/server/get-validator.go b/server/get-validator.go new file mode 100644 index 0000000..346a532 --- /dev/null +++ b/server/get-validator.go @@ -0,0 +1,44 @@ +package server + +import ( + "fmt" + "net/http" + + "github.com/gorilla/mux" + "github.com/nodeset-org/beacon-mock/api" +) + +// Handle a get validator request +func (s *BeaconMockServer) getValidator(w http.ResponseWriter, r *http.Request) { + // Get the request vars + _ = s.processApiRequest(w, r, nil) + vars := mux.Vars(r) + state, exists := vars[api.StateID] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing state ID")) + return + } + if state != "head" { + handleInputError(s.logger, w, fmt.Errorf("unsupported state ID [%s], only 'head' is supported", state)) + return + } + + id, exists := vars[api.ValidatorID] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing validator ID")) + return + } + + // Get the validator + validator, err := s.manager.GetValidator(id) + if err != nil { + handleInputError(s.logger, w, err) + return + } + + // Write the response + response := api.ValidatorResponse{ + Data: getValidatorMetaFromValidator(validator), + } + handleSuccess(w, s.logger, response) +} diff --git a/server/get-validators.go b/server/get-validators.go new file mode 100644 index 0000000..475046a --- /dev/null +++ b/server/get-validators.go @@ -0,0 +1,99 @@ +package server + +import ( + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "net/url" + "strings" + + "github.com/nodeset-org/beacon-mock/api" + "github.com/rocket-pool/node-manager-core/beacon/client" + "github.com/rocket-pool/node-manager-core/log" +) + +// Handle a get validators request +func (s *BeaconMockServer) getValidators(w http.ResponseWriter, r *http.Request) { + // Get the request vars + args := s.processApiRequest(w, r, nil) + + var ids []string + switch r.Method { + case http.MethodGet: + ids = s.getValidatorIDsFromRequestArgs(args) + case http.MethodPost: + ids = s.getValidatorIDsFromRequestBody(w, r) + if ids == nil { + return + } + default: + handleInvalidMethod(s.logger, w) + return + } + + // Get the validators + validators, err := s.manager.GetValidators(ids) + if err != nil { + handleInputError(s.logger, w, err) + return + } + + // Write the response + validatorMetas := make([]client.Validator, len(validators)) + for i, validator := range validators { + validatorMetas[i] = getValidatorMetaFromValidator(validator) + } + response := client.ValidatorsResponse{ + Data: validatorMetas, + } + handleSuccess(w, s.logger, response) +} + +// Get all of the validator IDs from the request query for a GET request +func (s *BeaconMockServer) getValidatorIDsFromRequestArgs(args url.Values) []string { + ids := args["id"] + return getValidatorIDs(ids) +} + +// Get all of the validator IDs from the request body for a POST request +func (s *BeaconMockServer) getValidatorIDsFromRequestBody(w http.ResponseWriter, r *http.Request) []string { + // Read the body + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + handleInputError(s.logger, w, fmt.Errorf("error reading request body: %w", err)) + return nil + } + s.logger.Debug("Request body:", slog.String(log.BodyKey, string(bodyBytes))) + + // Deserialize the body + var requestBody api.ValidatorsRequest + err = json.Unmarshal(bodyBytes, &requestBody) + if err != nil { + handleInputError(s.logger, w, fmt.Errorf("error deserializing request body: %w", err)) + return nil + } + + return getValidatorIDs(requestBody.IDs) +} + +// Get all of the validator IDs from a list of them, handling the case where they're comma-separated +func getValidatorIDs(ids []string) []string { + if len(ids) == 0 { + return []string{} + } + + fullIds := make([]string, 0, len(ids)) + for _, id := range ids { + elements := strings.Split(id, ",") + for _, element := range elements { + trimmed := strings.TrimSpace(element) + if trimmed == "" { + continue + } + fullIds = append(fullIds, trimmed) + } + } + return fullIds +} diff --git a/server/response-handling.go b/server/response-handling.go new file mode 100644 index 0000000..94bd497 --- /dev/null +++ b/server/response-handling.go @@ -0,0 +1,88 @@ +package server + +import ( + "fmt" + "log/slog" + "net/http" + + "github.com/goccy/go-json" + "github.com/nodeset-org/beacon-mock/api" + "github.com/rocket-pool/node-manager-core/log" +) + +// Handle routes called with an invalid method +func handleInvalidMethod(logger *slog.Logger, w http.ResponseWriter) { + writeResponse(w, logger, http.StatusMethodNotAllowed, []byte{}) +} + +// Handles an error related to parsing the input parameters of a request +func handleInputError(logger *slog.Logger, w http.ResponseWriter, err error) { + msg := err.Error() + code := http.StatusBadRequest + bytes := formatError(code, msg) + writeResponse(w, logger, code, bytes) +} + +// Write an error if the auth header couldn't be decoded +func handleServerError(w http.ResponseWriter, logger *slog.Logger, err error) { + msg := err.Error() + code := http.StatusInternalServerError + bytes := formatError(code, msg) + writeResponse(w, logger, code, bytes) +} + +// The request completed successfully +func handleSuccess(w http.ResponseWriter, logger *slog.Logger, message any) { + bytes := []byte{} + if message != nil { + // Serialize the response + var err error + bytes, err = json.Marshal(message) + if err != nil { + handleServerError(w, logger, fmt.Errorf("error serializing response: %w", err)) + } + } + + // Write it + logger.Debug("Response body", slog.String(log.BodyKey, string(bytes))) + writeResponse(w, logger, http.StatusOK, bytes) +} + +// Writes a response to an HTTP request back to the client and logs it +func writeResponse(w http.ResponseWriter, logger *slog.Logger, statusCode int, message []byte) { + // Prep the log attributes + codeMsg := fmt.Sprintf("%d %s", statusCode, http.StatusText(statusCode)) + attrs := []any{ + slog.String(log.CodeKey, codeMsg), + } + + // Log the response + logMsg := "Responded with:" + switch statusCode { + case http.StatusOK: + logger.Info(logMsg, attrs...) + case http.StatusInternalServerError: + logger.Error(logMsg, attrs...) + default: + logger.Warn(logMsg, attrs...) + } + + // Write it to the client + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(statusCode) + _, writeErr := w.Write(message) + if writeErr != nil { + logger.Error("Error writing response", "error", writeErr) + } +} + +// JSONifies an error for responding to requests +func formatError(code int, message string) []byte { + msg := api.ErrorResponse{ + Code: code, + Message: message, + } + + bytes, _ := json.Marshal(msg) + return bytes +} diff --git a/server/server.go b/server/server.go index d03b15c..0b5ca9f 100644 --- a/server/server.go +++ b/server/server.go @@ -1,16 +1,22 @@ package server import ( + "context" + "encoding/json" + "errors" "fmt" + "io" "log/slog" "net" "net/http" - "strconv" - "strings" + "net/url" + "sync" "github.com/gorilla/mux" + "github.com/nodeset-org/beacon-mock/api" + "github.com/nodeset-org/beacon-mock/db" "github.com/nodeset-org/beacon-mock/manager" - "github.com/rocket-pool/node-manager-core/beacon" + "github.com/rocket-pool/node-manager-core/log" ) type BeaconMockServer struct { @@ -23,30 +29,153 @@ type BeaconMockServer struct { manager *manager.BeaconMockManager } +func NewBeaconMockServer(logger *slog.Logger, ip string, port uint16, config *db.Config) (*BeaconMockServer, error) { + // Create the router + router := mux.NewRouter() + + // Create the manager + server := &BeaconMockServer{ + logger: logger, + ip: ip, + port: port, + router: router, + server: http.Server{ + Handler: router, + }, + manager: manager.NewBeaconMockManager(logger, config), + } + + // Register each route + apiRouter := router.PathPrefix("/eth").Subrouter() + server.registerApiRoutes(apiRouter) + adminRouter := router.PathPrefix("/admin").Subrouter() + server.registerAdminRoutes(adminRouter) + return server, nil +} + +// Starts listening for incoming HTTP requests +func (s *BeaconMockServer) Start(wg *sync.WaitGroup) error { + // Create the socket + socket, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.ip, s.port)) + if err != nil { + return fmt.Errorf("error creating socket: %w", err) + } + s.socket = socket + + // Get the port if random + if s.port == 0 { + s.port = uint16(socket.Addr().(*net.TCPAddr).Port) + } + + // Start listening + wg.Add(1) + go func() { + err := s.server.Serve(socket) + if !errors.Is(err, http.ErrServerClosed) { + s.logger.Error("error while listening for HTTP requests", log.Err(err)) + } + wg.Done() + }() + + return nil +} + +// Stops the HTTP listener +func (s *BeaconMockServer) Stop() error { + err := s.server.Shutdown(context.Background()) + if err != nil && !errors.Is(err, http.ErrServerClosed) { + return fmt.Errorf("error stopping listener: %w", err) + } + return nil +} + +// Get the port the server is listening on +func (s *BeaconMockServer) GetPort() uint16 { + return s.port +} + +// API routes +func (s *BeaconMockServer) registerApiRoutes(apiRouter *mux.Router) { + apiRouter.HandleFunc("/"+api.ValidatorsRoute, s.getValidators) + apiRouter.HandleFunc("/"+api.ValidatorRoute, func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + s.getValidator(w, r) + default: + handleInvalidMethod(s.logger, w) + } + }) + apiRouter.HandleFunc("/"+api.SyncingRoute, func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + s.getSyncStatus(w, r) + default: + handleInvalidMethod(s.logger, w) + } + }) +} + +// Admin routes +func (s *BeaconMockServer) registerAdminRoutes(adminRouter *mux.Router) { + adminRouter.HandleFunc("/"+api.SetBalanceRoute, func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + s.setBalance(w, r) + default: + handleInvalidMethod(s.logger, w) + } + }) + adminRouter.HandleFunc("/"+api.SetStatusRoute, func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + s.setStatus(w, r) + default: + handleInvalidMethod(s.logger, w) + } + }) + adminRouter.HandleFunc("/"+api.SlashRoute, func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + s.slash(w, r) + default: + handleInvalidMethod(s.logger, w) + } + }) + adminRouter.HandleFunc("/"+api.SetSlotRoute, func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + s.setSlot(w, r) + default: + handleInvalidMethod(s.logger, w) + } + }) +} + // ============= // === Utils === // ============= -func (s *BeaconMockServer) getValidatorByID(id string) (*beacon.ValidatorStatus, error) { - if strings.HasPrefix(id, "0x") { - pubkey, err := beacon.HexToValidatorPubkey(id) +func (s *BeaconMockServer) processApiRequest(w http.ResponseWriter, r *http.Request, requestBody any) url.Values { + args := r.URL.Query() + s.logger.Info("New request", slog.String(log.MethodKey, r.Method), slog.String(log.PathKey, r.URL.Path)) + s.logger.Debug("Request params:", slog.String(log.QueryKey, r.URL.RawQuery)) + + if requestBody != nil { + // Read the body + bodyBytes, err := io.ReadAll(r.Body) if err != nil { - return nil, err + handleInputError(s.logger, w, fmt.Errorf("error reading request body: %w", err)) + return nil } - validator := s.manager.Database.GetValidatorByPubkey(pubkey) - if validator == nil { - return nil, fmt.Errorf("validator with pubkey %s does not exist", pubkey.HexWithPrefix()) + s.logger.Debug("Request body:", slog.String(log.BodyKey, string(bodyBytes))) + + // Deserialize the body + err = json.Unmarshal(bodyBytes, &requestBody) + if err != nil { + handleInputError(s.logger, w, fmt.Errorf("error deserializing request body: %w", err)) + return nil } - return validator, nil } - index, err := strconv.ParseUint(id, 10, 64) - if err != nil { - return nil, err - } - validator := s.manager.Database.GetValidatorByIndex(uint(index)) - if validator == nil { - return nil, fmt.Errorf("validator with index %d does not exist", index) - } - return validator, nil + return args } diff --git a/server/server_test.go b/server/server_test.go new file mode 100644 index 0000000..0542965 --- /dev/null +++ b/server/server_test.go @@ -0,0 +1,108 @@ +package server + +import ( + "fmt" + "log/slog" + "net/http" + "os" + "sync" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/beacon-mock/db" + "github.com/stretchr/testify/require" +) + +const ( + DepositContractAddressString string = "0xde905175eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" +) + +// Various singleton variables used for testing +var ( + logger *slog.Logger = slog.Default() + server *BeaconMockServer = nil + wg *sync.WaitGroup = nil + port uint16 = 0 +) + +// Initialize a common server used by all tests +func TestMain(m *testing.M) { + // Create the config + depositContract := common.HexToAddress(DepositContractAddressString) + config := db.NewConfig(depositContract, true) + + // Create the server + var err error + server, err = NewBeaconMockServer(logger, "localhost", 0, config) + if err != nil { + fail("error creating server: %v", err) + } + logger.Info("Created server") + + // Start it + wg = &sync.WaitGroup{} + err = server.Start(wg) + if err != nil { + fail("error starting server: %v", err) + } + port = server.GetPort() + logger.Info(fmt.Sprintf("Started server on port %d", port)) + + // Run tests + code := m.Run() + + // Revert to the baseline after testing is done + cleanup() + + // Done + os.Exit(code) +} + +func fail(format string, args ...any) { + msg := fmt.Sprintf(format, args...) + logger.Error(msg) + cleanup() + os.Exit(1) +} + +func cleanup() { + if server != nil { + _ = server.Stop() + wg.Wait() + logger.Info("Stopped server") + } +} + +// ============= +// === Tests === +// ============= + +// Check for a 404 if requesting an unknown route +func TestUnknownRoute(t *testing.T) { + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Send a message without an auth header + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/eth/unknown_route", port), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the response + require.Equal(t, http.StatusNotFound, response.StatusCode) + t.Logf("Received not found status code") +} diff --git a/server/set-balance.go b/server/set-balance.go index a29d307..03aba04 100644 --- a/server/set-balance.go +++ b/server/set-balance.go @@ -1,8 +1,40 @@ package server -import "github.com/rocket-pool/node-manager-core/beacon" +import ( + "fmt" + "net/http" + "strconv" +) -func (s *BeaconMockServer) SetBalance(validator *beacon.ValidatorStatus, newBalanceGwei uint64) error { - validator.Balance = newBalanceGwei - return nil +func (s *BeaconMockServer) setBalance(w http.ResponseWriter, r *http.Request) { + // Get the request vars + args := s.processApiRequest(w, r, nil) + id, exists := args["id"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing validator ID")) + return + } + balanceString, exists := args["balance"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing balance")) + return + } + + // Input validation + balance, err := strconv.ParseUint(balanceString[0], 10, 64) + if err != nil { + handleInputError(s.logger, w, fmt.Errorf("invalid balance [%s]: %w", balanceString[0], err)) + return + } + + // Get the validator + validator, err := s.manager.GetValidator(id[0]) + if err != nil { + handleInputError(s.logger, w, err) + return + } + + // Set the balance + validator.SetBalance(balance) + handleSuccess(w, s.logger, nil) } diff --git a/server/set-slot.go b/server/set-slot.go new file mode 100644 index 0000000..260acc5 --- /dev/null +++ b/server/set-slot.go @@ -0,0 +1,28 @@ +package server + +import ( + "fmt" + "net/http" + "strconv" +) + +func (s *BeaconMockServer) setSlot(w http.ResponseWriter, r *http.Request) { + // Get the request vars + args := s.processApiRequest(w, r, nil) + slotString, exists := args["slot"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing slot")) + return + } + + // Input validation + slot, err := strconv.ParseUint(slotString[0], 10, 64) + if err != nil { + handleInputError(s.logger, w, fmt.Errorf("invalid slot [%s]: %w", slotString[0], err)) + return + } + + // Set the slot + s.manager.SetCurrentSlot(slot) + handleSuccess(w, s.logger, nil) +} diff --git a/server/set-status.go b/server/set-status.go index 9e5f90f..b837f6a 100644 --- a/server/set-status.go +++ b/server/set-status.go @@ -1,8 +1,42 @@ package server -import "github.com/rocket-pool/node-manager-core/beacon" +import ( + "fmt" + "net/http" -func (s *BeaconMockServer) SetStatus(validator *beacon.ValidatorStatus, newStatus beacon.ValidatorState) error { - validator.Status = newStatus - return nil + "github.com/rocket-pool/node-manager-core/beacon" +) + +func (s *BeaconMockServer) setStatus(w http.ResponseWriter, r *http.Request) { + // Get the request vars + args := s.processApiRequest(w, r, nil) + id, exists := args["id"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing validator ID")) + return + } + statusString, exists := args["status"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing status")) + return + } + + // Input validation + status := beacon.ValidatorState(statusString[0]) + if status == "" { + handleInputError(s.logger, w, fmt.Errorf("invalid status [%s]", statusString[0])) + return + + } + + // Get the validator + validator, err := s.manager.GetValidator(id[0]) + if err != nil { + handleInputError(s.logger, w, err) + return + } + + // Set the status + validator.SetStatus(status) + handleSuccess(w, s.logger, nil) } diff --git a/server/slash.go b/server/slash.go index 2ec5a0d..dec8efd 100644 --- a/server/slash.go +++ b/server/slash.go @@ -2,16 +2,43 @@ package server import ( "fmt" - - "github.com/rocket-pool/node-manager-core/beacon" + "net/http" + "strconv" ) -func (s *BeaconMockServer) SlashValidator(validator *beacon.ValidatorStatus, penaltyGwei uint64) error { - if validator.Status != beacon.ValidatorState_ActiveOngoing && validator.Status != beacon.ValidatorState_ActiveExiting { - return fmt.Errorf("validator with pubkey %s is not in a slashable state", validator.Pubkey.HexWithPrefix()) +func (s *BeaconMockServer) slash(w http.ResponseWriter, r *http.Request) { + // Get the request vars + args := s.processApiRequest(w, r, nil) + id, exists := args["id"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing validator ID")) + return + } + penaltyString, exists := args["penalty"] + if !exists { + handleInputError(s.logger, w, fmt.Errorf("missing penalty")) + return + } + + // Input validation + penalty, err := strconv.ParseUint(penaltyString[0], 10, 64) + if err != nil { + handleInputError(s.logger, w, fmt.Errorf("invalid penalty [%s]: %w", penaltyString[0], err)) + return + } + + // Get the validator + validator, err := s.manager.GetValidator(id[0]) + if err != nil { + handleInputError(s.logger, w, err) + return + } + + // Slash the validator + err = validator.Slash(penalty) + if err != nil { + handleInputError(s.logger, w, err) + return } - validator.Slashed = true - validator.Balance -= penaltyGwei - validator.Status = beacon.ValidatorState_ActiveSlashed - return nil + handleSuccess(w, s.logger, nil) } diff --git a/server/utils.go b/server/utils.go new file mode 100644 index 0000000..ec10a48 --- /dev/null +++ b/server/utils.go @@ -0,0 +1,25 @@ +package server + +import ( + "strconv" + + "github.com/nodeset-org/beacon-mock/db" + "github.com/rocket-pool/node-manager-core/beacon/client" +) + +func getValidatorMetaFromValidator(validator *db.Validator) client.Validator { + validatorMeta := client.Validator{ + Index: strconv.FormatUint(validator.Index, 10), + Balance: client.Uinteger(validator.Balance), + Status: string(validator.Status), + } + validatorMeta.Validator.Pubkey = validator.Pubkey[:] + validatorMeta.Validator.WithdrawalCredentials = validator.WithdrawalCredentials[:] + validatorMeta.Validator.EffectiveBalance = client.Uinteger(validator.EffectiveBalance) + validatorMeta.Validator.Slashed = validator.Slashed + validatorMeta.Validator.ActivationEligibilityEpoch = client.Uinteger(validator.ActivationEligibilityEpoch) + validatorMeta.Validator.ActivationEpoch = client.Uinteger(validator.ActivationEpoch) + validatorMeta.Validator.ExitEpoch = client.Uinteger(validator.ExitEpoch) + validatorMeta.Validator.WithdrawableEpoch = client.Uinteger(validator.WithdrawableEpoch) + return validatorMeta +} From d68d57ad0b9dc4fb45bba60c3e8f96bbdc0d3951 Mon Sep 17 00:00:00 2001 From: Joe Clapis Date: Wed, 22 May 2024 23:33:05 -0400 Subject: [PATCH 3/6] Added more tests --- api/routes.go | 8 ++- go.mod | 2 +- go.sum | 8 +-- server/get-sync-status_test.go | 52 ++++++++++++-- server/get-validator_test.go | 119 +++++++++++++++++++++++++++++++++ server/get-validators_test.go | 1 + 6 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 server/get-validator_test.go create mode 100644 server/get-validators_test.go diff --git a/api/routes.go b/api/routes.go index 49f7600..5b97a1c 100644 --- a/api/routes.go +++ b/api/routes.go @@ -5,9 +5,11 @@ const ( ValidatorID string = "validator_id" // Beacon API routes - ValidatorsRoute string = "v1/beacon/states/{" + StateID + "}/validators" - ValidatorRoute string = "v1/beacon/states/{" + StateID + "}/validators/{" + ValidatorID + "}" - SyncingRoute string = "v1/node/syncing" + ValidatorsRouteTemplate string = "v1/beacon/states/%s/validators" + ValidatorsRoute string = "v1/beacon/states/{state_id}/validators" + ValidatorRouteTemplate string = "v1/beacon/states/%s/validators/%s" + ValidatorRoute string = "v1/beacon/states/{state_id}/validators/{validator_id}" + SyncingRoute string = "v1/node/syncing" // Admin routes AddValidatorRoute string = "add-validator" diff --git a/go.mod b/go.mod index 6d0dece..e6ca3dc 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ethereum/go-ethereum v1.14.3 github.com/goccy/go-json v0.10.2 github.com/gorilla/mux v1.8.1 - github.com/rocket-pool/node-manager-core v0.3.1-0.20240522174819-feb0ab2cc75f + github.com/rocket-pool/node-manager-core v0.3.1-0.20240523031437-fea797b95b9c github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 04ac5a7..b2823be 100644 --- a/go.sum +++ b/go.sum @@ -56,8 +56,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etly github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/ethereum/c-kzg-4844 v1.0.1 h1:pGixCbGizcVKSwoV70ge48+PrbB+iSKs2rjgfE4yJmQ= github.com/ethereum/c-kzg-4844 v1.0.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.14.0 h1:xRWC5NlB6g1x7vNy4HDBLuqVNbtLrc7v8S6+Uxim1LU= -github.com/ethereum/go-ethereum v1.14.0/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= github.com/ethereum/go-ethereum v1.14.3 h1:5zvnAqLtnCZrU9uod1JCvHWJbPMURzYFHfc2eHz4PHA= github.com/ethereum/go-ethereum v1.14.3/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= @@ -188,10 +186,8 @@ github.com/prysmaticlabs/prysm/v5 v5.0.3 h1:hUi0gu6v7aXmMQkl2GbrLoWcMhDNIbkVxRwr github.com/prysmaticlabs/prysm/v5 v5.0.3/go.mod h1:v5Oz4A4cWljfxUmW7SDk/VBzoYnei+lzwJogvSqUZVs= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rocket-pool/node-manager-core v0.3.1-0.20240522174819-feb0ab2cc75f h1:wyVBEQwHcfhi4xU6DS6oeq4y+vUmxp3/+JdYSWLi9cQ= -github.com/rocket-pool/node-manager-core v0.3.1-0.20240522174819-feb0ab2cc75f/go.mod h1:Clii5aca9PvR4HoAlUs8dh2OsJbDDnJ4yL5EaQE1gSo= -github.com/rocket-pool/node-manager-core v0.3.1 h1:9M6HFH4Kgy1wYUYqrh3+A1RdSe/HjndIWMBUBhjxVk0= -github.com/rocket-pool/node-manager-core v0.3.1/go.mod h1:f/w3jnzi3ipXet7acZ0pQhGbolU/g3IDVrqXcNWuHw4= +github.com/rocket-pool/node-manager-core v0.3.1-0.20240523031437-fea797b95b9c h1:zev2m0KaBVASAmBAZtGL8pI/VNM8T5N71HUVHYBduuw= +github.com/rocket-pool/node-manager-core v0.3.1-0.20240523031437-fea797b95b9c/go.mod h1:Clii5aca9PvR4HoAlUs8dh2OsJbDDnJ4yL5EaQE1gSo= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= diff --git a/server/get-sync-status_test.go b/server/get-sync-status_test.go index b4233ab..2b7368a 100644 --- a/server/get-sync-status_test.go +++ b/server/get-sync-status_test.go @@ -13,8 +13,8 @@ import ( "github.com/stretchr/testify/require" ) -// Make sure sync status requests work -func TestSyncStatus(t *testing.T) { +// Make sure sync status requests work when synced +func TestSynced(t *testing.T) { currentSlot := uint64(12) // Take a snapshot @@ -31,6 +31,47 @@ func TestSyncStatus(t *testing.T) { server.manager.SetDatabase(d) server.manager.SetCurrentSlot(currentSlot) + // Send a sync status request + parsedResponse := getSyncStatusResponse(t) + + // Make sure the response is correct + require.Equal(t, currentSlot, uint64(parsedResponse.Data.HeadSlot)) + require.Equal(t, uint64(0), uint64(parsedResponse.Data.SyncDistance)) + require.False(t, parsedResponse.Data.IsSyncing) + t.Logf("Received correct response - head slot: %d, sync distance: %d, is syncing: %t", parsedResponse.Data.HeadSlot, parsedResponse.Data.SyncDistance, parsedResponse.Data.IsSyncing) +} + +// Make sure sync status requests work when still syncing +func TestUnsynced(t *testing.T) { + currentSlot := uint64(8) + headSlot := uint64(12) + + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + server.manager.SetCurrentSlot(headSlot) + server.manager.SetCurrentSlot(currentSlot) + + // Send a sync status request + parsedResponse := getSyncStatusResponse(t) + + // Make sure the response is correct + require.Equal(t, headSlot, uint64(parsedResponse.Data.HeadSlot)) + require.Equal(t, headSlot-currentSlot, uint64(parsedResponse.Data.SyncDistance)) + require.True(t, parsedResponse.Data.IsSyncing) + t.Logf("Received correct response - head slot: %d, sync distance: %d, is syncing: %t", parsedResponse.Data.HeadSlot, parsedResponse.Data.SyncDistance, parsedResponse.Data.IsSyncing) +} + +func getSyncStatusResponse(t *testing.T) client.SyncStatusResponse { // Create the request request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/eth/%s", port, api.SyncingRoute), nil) if err != nil { @@ -61,9 +102,6 @@ func TestSyncStatus(t *testing.T) { t.Fatalf("error deserializing response: %v", err) } - // Make sure the response is correct - require.Equal(t, currentSlot, uint64(parsedResponse.Data.HeadSlot)) - require.Equal(t, uint64(0), uint64(parsedResponse.Data.SyncDistance)) - require.False(t, parsedResponse.Data.IsSyncing) - t.Logf("Received correct response - head slot: %d, sync distance: %d, is syncing: %t", parsedResponse.Data.HeadSlot, parsedResponse.Data.SyncDistance, parsedResponse.Data.IsSyncing) + t.Log("Parsed response") + return parsedResponse } diff --git a/server/get-validator_test.go b/server/get-validator_test.go new file mode 100644 index 0000000..f99319f --- /dev/null +++ b/server/get-validator_test.go @@ -0,0 +1,119 @@ +package server + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/beacon-mock/api" + "github.com/nodeset-org/beacon-mock/db" + idb "github.com/nodeset-org/beacon-mock/internal/db" + "github.com/rocket-pool/node-manager-core/beacon" + "github.com/rocket-pool/node-manager-core/beacon/client" + "github.com/stretchr/testify/require" +) + +// Test getting a validator by index +func TestValidatorIndex(t *testing.T) { + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send a validator status request + id := uint(1) + v := d.GetValidatorByIndex(id) + require.NotNil(t, v) + parsedResponse := getValidatorResponse(t, strconv.FormatUint(uint64(id), 10)) + + // Make sure the response is correct + compareValidators(t, v, &parsedResponse.Data) + t.Log("Validators matched") +} + +// Test getting a validator by pubkey +func TestValidatorPubkey(t *testing.T) { + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send a validator status request + v := d.GetValidatorByIndex(2) + require.NotNil(t, v) + parsedResponse := getValidatorResponse(t, v.Pubkey.HexWithPrefix()) + + // Make sure the response is correct + compareValidators(t, v, &parsedResponse.Data) + t.Log("Validators matched") +} + +// Round trip a validator status request +func getValidatorResponse(t *testing.T, id string) api.ValidatorResponse { + // Create the request + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/eth/%s", port, fmt.Sprintf(api.ValidatorRouteTemplate, "head", id)), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") + + // Read the body + defer response.Body.Close() + bytes, err := io.ReadAll(response.Body) + if err != nil { + t.Fatalf("error reading the response body: %v", err) + } + var parsedResponse api.ValidatorResponse + err = json.Unmarshal(bytes, &parsedResponse) + if err != nil { + t.Fatalf("error deserializing response: %v", err) + } + + t.Log("Parsed response") + return parsedResponse +} + +func compareValidators(t *testing.T, local *db.Validator, remote *client.Validator) { + require.Equal(t, strconv.FormatUint(local.Index, 10), remote.Index) + require.Equal(t, local.Balance, uint64(remote.Balance)) + require.Equal(t, string(local.Status), remote.Status) + require.Equal(t, local.Pubkey, beacon.ValidatorPubkey(remote.Validator.Pubkey)) + require.Equal(t, local.WithdrawalCredentials, common.BytesToHash(remote.Validator.WithdrawalCredentials)) + require.Equal(t, local.EffectiveBalance, uint64(remote.Validator.EffectiveBalance)) + require.Equal(t, local.Slashed, remote.Validator.Slashed) + require.Equal(t, local.ActivationEligibilityEpoch, uint64(remote.Validator.ActivationEligibilityEpoch)) + require.Equal(t, local.ActivationEpoch, uint64(remote.Validator.ActivationEpoch)) + require.Equal(t, local.ExitEpoch, uint64(remote.Validator.ExitEpoch)) + require.Equal(t, local.WithdrawableEpoch, uint64(remote.Validator.WithdrawableEpoch)) +} diff --git a/server/get-validators_test.go b/server/get-validators_test.go new file mode 100644 index 0000000..abb4e43 --- /dev/null +++ b/server/get-validators_test.go @@ -0,0 +1 @@ +package server From 544a9e3f8986c248182f638cb0d63d696332bc83 Mon Sep 17 00:00:00 2001 From: Joe Clapis Date: Thu, 23 May 2024 10:46:52 -0400 Subject: [PATCH 4/6] Finished API routes and unit tests --- internal/test/utils.go | 2 + server/add-validator_test.go | 94 +++++++++++++ server/get-sync-status_test.go | 3 +- server/get-validator_test.go | 3 +- server/get-validators.go | 3 +- server/get-validators_test.go | 244 +++++++++++++++++++++++++++++++++ server/server.go | 11 +- server/set-balance_test.go | 66 +++++++++ server/set-slot_test.go | 69 ++++++++++ server/set-status_test.go | 66 +++++++++ server/slash_test.go | 82 +++++++++++ 11 files changed, 639 insertions(+), 4 deletions(-) create mode 100644 server/add-validator_test.go create mode 100644 server/set-balance_test.go create mode 100644 server/set-slot_test.go create mode 100644 server/set-status_test.go create mode 100644 server/slash_test.go diff --git a/internal/test/utils.go b/internal/test/utils.go index 0bf6444..e318953 100644 --- a/internal/test/utils.go +++ b/internal/test/utils.go @@ -4,6 +4,8 @@ const ( Pubkey0String string = "0xbeac0900bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" Pubkey1String string = "0xbeac0901bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" Pubkey2String string = "0xbeac0902bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + Pubkey3String string = "0xbeac0903bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" WithdrawalCredentialsString string = "0xc12ed5eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + WithdrawalCredentials2String string = "0xc12ed52eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" DepositContractAddressString string = "0xde905175eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ) diff --git a/server/add-validator_test.go b/server/add-validator_test.go new file mode 100644 index 0000000..2bc5c07 --- /dev/null +++ b/server/add-validator_test.go @@ -0,0 +1,94 @@ +package server + +import ( + "fmt" + "io" + "net/http" + "strconv" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + "github.com/nodeset-org/beacon-mock/api" + idb "github.com/nodeset-org/beacon-mock/internal/db" + "github.com/nodeset-org/beacon-mock/internal/test" + "github.com/rocket-pool/node-manager-core/beacon" + "github.com/rocket-pool/node-manager-core/node/validator" + "github.com/stretchr/testify/require" +) + +// Test setting a validator's balance +func TestAddValidator(t *testing.T) { + pubkey, err := beacon.HexToValidatorPubkey(test.Pubkey3String) + if err != nil { + t.Fatalf("error converting pubkey [%s]: %v", test.Pubkey3String, err) + } + credsAddress := common.HexToAddress(test.WithdrawalCredentials2String) + creds := validator.GetWithdrawalCredsFromAddress(credsAddress) + + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send the set balance request + parsedResponse := getAddValidatorResponse(t, pubkey, creds) + require.Equal(t, uint64(3), parsedResponse.Index) + t.Logf("Validator added with index %d", parsedResponse.Index) + + // Get the validator's status now + id := strconv.FormatUint(parsedResponse.Index, 10) + statusResponse := getValidatorResponse(t, id) + + // Make sure the response is correct + require.Equal(t, pubkey, beacon.ValidatorPubkey(statusResponse.Data.Validator.Pubkey)) + require.Equal(t, creds, common.BytesToHash(statusResponse.Data.Validator.WithdrawalCredentials)) + t.Logf("Received correct response - pubkey: %s, creds: %s", pubkey.HexWithPrefix(), creds.Hex()) +} + +func getAddValidatorResponse(t *testing.T, pubkey beacon.ValidatorPubkey, creds common.Hash) api.AddValidatorResponse { + // Create the request + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/admin/%s", port, api.AddValidatorRoute), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + query := request.URL.Query() + query.Add("pubkey", pubkey.HexWithPrefix()) + query.Add("creds", creds.Hex()) + request.URL.RawQuery = query.Encode() + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") + + // Read the body + defer response.Body.Close() + bytes, err := io.ReadAll(response.Body) + if err != nil { + t.Fatalf("error reading the response body: %v", err) + } + var parsedResponse api.AddValidatorResponse + err = json.Unmarshal(bytes, &parsedResponse) + if err != nil { + t.Fatalf("error deserializing response: %v", err) + } + + t.Log("Parsed response") + return parsedResponse +} diff --git a/server/get-sync-status_test.go b/server/get-sync-status_test.go index 2b7368a..77933e4 100644 --- a/server/get-sync-status_test.go +++ b/server/get-sync-status_test.go @@ -1,12 +1,13 @@ package server import ( - "encoding/json" "fmt" "io" "net/http" "testing" + "github.com/goccy/go-json" + "github.com/nodeset-org/beacon-mock/api" idb "github.com/nodeset-org/beacon-mock/internal/db" "github.com/rocket-pool/node-manager-core/beacon/client" diff --git a/server/get-validator_test.go b/server/get-validator_test.go index f99319f..9ae5f38 100644 --- a/server/get-validator_test.go +++ b/server/get-validator_test.go @@ -1,13 +1,14 @@ package server import ( - "encoding/json" "fmt" "io" "net/http" "strconv" "testing" + "github.com/goccy/go-json" + "github.com/ethereum/go-ethereum/common" "github.com/nodeset-org/beacon-mock/api" "github.com/nodeset-org/beacon-mock/db" diff --git a/server/get-validators.go b/server/get-validators.go index 475046a..c931f69 100644 --- a/server/get-validators.go +++ b/server/get-validators.go @@ -1,7 +1,6 @@ package server import ( - "encoding/json" "fmt" "io" "log/slog" @@ -9,6 +8,8 @@ import ( "net/url" "strings" + "github.com/goccy/go-json" + "github.com/nodeset-org/beacon-mock/api" "github.com/rocket-pool/node-manager-core/beacon/client" "github.com/rocket-pool/node-manager-core/log" diff --git a/server/get-validators_test.go b/server/get-validators_test.go index abb4e43..abe2795 100644 --- a/server/get-validators_test.go +++ b/server/get-validators_test.go @@ -1 +1,245 @@ package server + +import ( + "bytes" + "fmt" + "io" + "net/http" + "strconv" + "testing" + + "github.com/goccy/go-json" + "github.com/nodeset-org/beacon-mock/api" + idb "github.com/nodeset-org/beacon-mock/internal/db" + "github.com/rocket-pool/node-manager-core/beacon/client" + "github.com/stretchr/testify/require" +) + +// Test getting all validators +func TestAllValidators(t *testing.T) { + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send a validator status request + parsedResponse := getValidatorsResponse(t, nil) + + // Make sure the response is correct + require.Len(t, parsedResponse.Data, 3) + compareValidators(t, d.GetValidatorByIndex(0), &parsedResponse.Data[0]) + compareValidators(t, d.GetValidatorByIndex(1), &parsedResponse.Data[1]) + compareValidators(t, d.GetValidatorByIndex(2), &parsedResponse.Data[2]) + t.Log("Validators matched") +} + +// Test getting 1 validator by index +func TestValidatorsByIndex_1(t *testing.T) { + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send a validator status request + id := uint(1) + v := d.GetValidatorByIndex(id) + ids := []string{ + strconv.FormatUint(uint64(id), 10), + } + parsedResponse := getValidatorsResponse(t, ids) + + // Make sure the response is correct + require.Len(t, parsedResponse.Data, 1) + compareValidators(t, v, &parsedResponse.Data[0]) + t.Log("Validators matched") +} + +// Test getting 1 validator by pubkey +func TestValidatorsByIndex_Pubkey(t *testing.T) { + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send a validator status request + v := d.GetValidatorByIndex(1) + ids := []string{ + v.Pubkey.HexWithPrefix(), + } + parsedResponse := getValidatorsResponse(t, ids) + + // Make sure the response is correct + require.Len(t, parsedResponse.Data, 1) + compareValidators(t, v, &parsedResponse.Data[0]) + t.Log("Validators matched") +} + +// Test getting multiple validators but not all +func TestValidatorsByIndex_Multi(t *testing.T) { + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send a validator status request + id0 := uint(0) + v0 := d.GetValidatorByIndex(id0) + v2 := d.GetValidatorByIndex(2) + ids := []string{ + strconv.FormatUint(uint64(id0), 10), + v2.Pubkey.HexWithPrefix(), + } + parsedResponse := getValidatorsResponse(t, ids) + + // Make sure the response is correct + require.Len(t, parsedResponse.Data, 2) + compareValidators(t, v0, &parsedResponse.Data[0]) + compareValidators(t, v2, &parsedResponse.Data[1]) + t.Log("Validators matched") +} + +// Test getting multiple validators but not all via POST +func TestValidatorsByIndex_Post(t *testing.T) { + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send a validator status request + id0 := uint(0) + v0 := d.GetValidatorByIndex(id0) + v2 := d.GetValidatorByIndex(2) + ids := []string{ + strconv.FormatUint(uint64(id0), 10), + v2.Pubkey.HexWithPrefix(), + } + parsedResponse := getValidatorsResponse(t, ids) + + // Make sure the response is correct + require.Len(t, parsedResponse.Data, 2) + compareValidators(t, v0, &parsedResponse.Data[0]) + compareValidators(t, v2, &parsedResponse.Data[1]) + t.Log("Validators matched") +} + +// Round trip a validators status request +func getValidatorsResponse(t *testing.T, ids []string) client.ValidatorsResponse { + // Create the request + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/eth/%s", port, fmt.Sprintf(api.ValidatorsRouteTemplate, "head")), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + query := request.URL.Query() + query["id"] = ids + request.URL.RawQuery = query.Encode() + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") + + // Read the body + defer response.Body.Close() + bytes, err := io.ReadAll(response.Body) + if err != nil { + t.Fatalf("error reading the response body: %v", err) + } + var parsedResponse client.ValidatorsResponse + err = json.Unmarshal(bytes, &parsedResponse) + if err != nil { + t.Fatalf("error deserializing response: %v", err) + } + + t.Log("Parsed response") + return parsedResponse +} + +// Round trip a validators status request via a POST +func getValidatorsResponsePost(t *testing.T, ids []string) client.ValidatorsResponse { + // Create the request + reqBody := api.ValidatorsRequest{ + IDs: ids, + } + reqBodyBytes, err := json.Marshal(reqBody) + if err != nil { + t.Fatalf("error serializing request body: %v", err) + } + request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:%d/eth/%s", port, fmt.Sprintf(api.ValidatorsRouteTemplate, "head")), bytes.NewReader(reqBodyBytes)) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") + + // Read the body + defer response.Body.Close() + bytes, err := io.ReadAll(response.Body) + if err != nil { + t.Fatalf("error reading the response body: %v", err) + } + var parsedResponse client.ValidatorsResponse + err = json.Unmarshal(bytes, &parsedResponse) + if err != nil { + t.Fatalf("error deserializing response: %v", err) + } + + t.Log("Parsed response") + return parsedResponse +} diff --git a/server/server.go b/server/server.go index 0b5ca9f..9e2aaf8 100644 --- a/server/server.go +++ b/server/server.go @@ -2,7 +2,6 @@ package server import ( "context" - "encoding/json" "errors" "fmt" "io" @@ -12,6 +11,8 @@ import ( "net/url" "sync" + "github.com/goccy/go-json" + "github.com/gorilla/mux" "github.com/nodeset-org/beacon-mock/api" "github.com/nodeset-org/beacon-mock/db" @@ -117,6 +118,14 @@ func (s *BeaconMockServer) registerApiRoutes(apiRouter *mux.Router) { // Admin routes func (s *BeaconMockServer) registerAdminRoutes(adminRouter *mux.Router) { + adminRouter.HandleFunc("/"+api.AddValidatorRoute, func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + s.addValidator(w, r) + default: + handleInvalidMethod(s.logger, w) + } + }) adminRouter.HandleFunc("/"+api.SetBalanceRoute, func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: diff --git a/server/set-balance_test.go b/server/set-balance_test.go new file mode 100644 index 0000000..588bc87 --- /dev/null +++ b/server/set-balance_test.go @@ -0,0 +1,66 @@ +package server + +import ( + "fmt" + "net/http" + "strconv" + "testing" + + "github.com/nodeset-org/beacon-mock/api" + idb "github.com/nodeset-org/beacon-mock/internal/db" + "github.com/stretchr/testify/require" +) + +// Test setting a validator's balance +func TestSetBalance(t *testing.T) { + balance := uint64(33e9) + + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + v1 := d.GetValidatorByIndex(1) + id := v1.Pubkey.HexWithPrefix() + + // Send the set balance request + sendSetBalanceRequest(t, id, balance) + + // Get the validator's status now + parsedResponse := getValidatorResponse(t, id) + + // Make sure the response is correct + require.Equal(t, balance, uint64(parsedResponse.Data.Balance)) + t.Logf("Received correct response - balance: %d", parsedResponse.Data.Balance) +} + +func sendSetBalanceRequest(t *testing.T, id string, balance uint64) { + // Create the request + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/admin/%s", port, api.SetBalanceRoute), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + query := request.URL.Query() + query.Add("id", id) + query.Add("balance", strconv.FormatUint(balance, 10)) + request.URL.RawQuery = query.Encode() + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") +} diff --git a/server/set-slot_test.go b/server/set-slot_test.go new file mode 100644 index 0000000..6a7ef26 --- /dev/null +++ b/server/set-slot_test.go @@ -0,0 +1,69 @@ +package server + +import ( + "fmt" + "net/http" + "strconv" + "testing" + + "github.com/nodeset-org/beacon-mock/api" + idb "github.com/nodeset-org/beacon-mock/internal/db" + "github.com/stretchr/testify/require" +) + +// Test setting the current slot +func TestSetSlot(t *testing.T) { + headSlot := uint64(14) + currentSlot := uint64(9) + + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + + // Send the head slot request + sendSetSlotRequest(t, headSlot) + + // Send the current slot request + sendSetSlotRequest(t, currentSlot) + + // Get the sync status now + parsedResponse := getSyncStatusResponse(t) + + // Make sure the response is correct + require.Equal(t, headSlot, uint64(parsedResponse.Data.HeadSlot)) + require.Equal(t, (headSlot - currentSlot), uint64(parsedResponse.Data.SyncDistance)) + require.True(t, parsedResponse.Data.IsSyncing) + t.Logf("Received correct response - head slot: %d, sync distance: %d, is syncing: %t", parsedResponse.Data.HeadSlot, parsedResponse.Data.SyncDistance, parsedResponse.Data.IsSyncing) +} + +func sendSetSlotRequest(t *testing.T, slot uint64) { + // Create the request + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/admin/%s", port, api.SetSlotRoute), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + query := request.URL.Query() + query.Add("slot", strconv.FormatUint(slot, 10)) + request.URL.RawQuery = query.Encode() + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") +} diff --git a/server/set-status_test.go b/server/set-status_test.go new file mode 100644 index 0000000..0d98074 --- /dev/null +++ b/server/set-status_test.go @@ -0,0 +1,66 @@ +package server + +import ( + "fmt" + "net/http" + "testing" + + "github.com/nodeset-org/beacon-mock/api" + idb "github.com/nodeset-org/beacon-mock/internal/db" + "github.com/rocket-pool/node-manager-core/beacon" + "github.com/stretchr/testify/require" +) + +// Test setting a validator's status +func TestSetStatus(t *testing.T) { + status := beacon.ValidatorState_ActiveOngoing + + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + v1 := d.GetValidatorByIndex(1) + id := v1.Pubkey.HexWithPrefix() + + // Send the set status request + sendSetStatusRequest(t, id, status) + + // Get the validator's status now + parsedResponse := getValidatorResponse(t, id) + + // Make sure the response is correct + require.Equal(t, string(status), parsedResponse.Data.Status) + t.Logf("Received correct response - status: %s", parsedResponse.Data.Status) +} + +func sendSetStatusRequest(t *testing.T, id string, status beacon.ValidatorState) { + // Create the request + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/admin/%s", port, api.SetStatusRoute), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + query := request.URL.Query() + query.Add("id", id) + query.Add("status", string(status)) + request.URL.RawQuery = query.Encode() + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") +} diff --git a/server/slash_test.go b/server/slash_test.go new file mode 100644 index 0000000..1a7efef --- /dev/null +++ b/server/slash_test.go @@ -0,0 +1,82 @@ +package server + +import ( + "fmt" + "net/http" + "strconv" + "testing" + + "github.com/nodeset-org/beacon-mock/api" + idb "github.com/nodeset-org/beacon-mock/internal/db" + "github.com/rocket-pool/node-manager-core/beacon" + "github.com/stretchr/testify/require" +) + +// Test slashing a validator +func TestSlash(t *testing.T) { + penalty := uint64(1e9) + + // Take a snapshot + server.manager.TakeSnapshot("test") + defer func() { + err := server.manager.RevertToSnapshot("test") + if err != nil { + t.Fatalf("error reverting to snapshot: %v", err) + } + }() + + // Provision the database + d := idb.ProvisionDatabaseForTesting(t, logger) + server.manager.SetDatabase(d) + v1 := d.GetValidatorByIndex(1) + id := v1.Pubkey.HexWithPrefix() + + // Make the validator active + sendSetStatusRequest(t, id, beacon.ValidatorState_ActiveOngoing) + t.Log("Marked the validator as active") + + // Get the original validator's status + parsedResponse := getValidatorResponse(t, id) + + // Make sure the response is correct + require.Equal(t, string(beacon.ValidatorState_ActiveOngoing), parsedResponse.Data.Status) + require.Equal(t, uint64(32e9), uint64(parsedResponse.Data.Balance)) + require.False(t, parsedResponse.Data.Validator.Slashed) + t.Logf("Original status is correct - status: %s", parsedResponse.Data.Status) + + // Send the slash request + sendSlashRequest(t, id, penalty) + + // Get the validator's status now + parsedResponse = getValidatorResponse(t, id) + + // Make sure the response is correct + require.Equal(t, string(beacon.ValidatorState_ActiveSlashed), parsedResponse.Data.Status) + require.Equal(t, uint64(32e9)-penalty, uint64(parsedResponse.Data.Balance)) + require.True(t, parsedResponse.Data.Validator.Slashed) + t.Logf("Received correct response - status: %s, balance: %d, slashed: %t", parsedResponse.Data.Status, parsedResponse.Data.Balance, parsedResponse.Data.Validator.Slashed) +} + +func sendSlashRequest(t *testing.T, id string, penalty uint64) { + // Create the request + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/admin/%s", port, api.SlashRoute), nil) + if err != nil { + t.Fatalf("error creating request: %v", err) + } + query := request.URL.Query() + query.Add("id", id) + query.Add("penalty", strconv.FormatUint(penalty, 10)) + request.URL.RawQuery = query.Encode() + t.Logf("Created request") + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("error sending request: %v", err) + } + t.Logf("Sent request") + + // Check the status code + require.Equal(t, http.StatusOK, response.StatusCode) + t.Logf("Received OK status code") +} From 9f975c9092f60a219c1f0460aae6bbe0586b16e1 Mon Sep 17 00:00:00 2001 From: Joe Clapis Date: Thu, 23 May 2024 11:33:07 -0400 Subject: [PATCH 5/6] Added a CLI --- cli.go | 139 ++++++++++++++++++++++++++++++++++ db/cfg_test.go | 5 +- db/config.go | 50 ++++++------ go.mod | 8 +- go.sum | 102 +++++++++++++++++++++++-- internal/test/utils.go | 1 - manager/manager.go | 5 -- server/get-validators_test.go | 2 +- server/server_test.go | 4 +- 9 files changed, 268 insertions(+), 48 deletions(-) create mode 100644 cli.go diff --git a/cli.go b/cli.go new file mode 100644 index 0000000..aa92bfe --- /dev/null +++ b/cli.go @@ -0,0 +1,139 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "log/slog" + "os" + "os/signal" + "sync" + "syscall" + + "github.com/nodeset-org/beacon-mock/db" + "github.com/nodeset-org/beacon-mock/server" + "github.com/urfave/cli/v2" +) + +const ( + Version string = "0.1.0" +) + +// Run +func main() { + // Initialise application + app := cli.NewApp() + + // Set application info + app.Name = "beacon-mock" + app.Usage = "Partial mock of a Beacon Chain client, useful for testing applications that use the validator status routes" + app.Version = Version + app.Authors = []*cli.Author{ + { + Name: "Nodeset", + Email: "info@nodeset.io", + }, + } + app.Copyright = "(C) 2024 NodeSet LLC" + + ipFlag := &cli.StringFlag{ + Name: "ip", + Aliases: []string{"i"}, + Usage: "The IP address to bind the API server to", + Value: "127.0.0.1", + } + portFlag := &cli.UintFlag{ + Name: "port", + Aliases: []string{"p"}, + Usage: "The port to bind the API server to", + Value: 48812, + } + configFlag := &cli.StringFlag{ + Name: "config-file", + Aliases: []string{"c"}, + Usage: "An optional configuration file to load. If not specified, defaults will be used", + } + + app.Flags = []cli.Flag{ + ipFlag, + portFlag, + configFlag, + } + app.Action = func(c *cli.Context) error { + logger := slog.Default() + + // Load the config file if specified + config := db.NewDefaultConfig() + configFile := c.String(configFlag.Name) + if configFile != "" { + // Make sure the file exists + _, err := os.Stat(configFile) + if errors.Is(err, os.ErrNotExist) { + fmt.Fprintf(os.Stderr, "Config file [%s] doesn't exist %v", configFile, err) + os.Exit(1) + } + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading config file: %v", err) + os.Exit(1) + } + + // Read the file + bytes, err := os.ReadFile(configFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading config file: %v", err) + os.Exit(1) + } + + // Unmarshal it + err = json.Unmarshal(bytes, &config) + if err != nil { + fmt.Fprintf(os.Stderr, "Error unmarshalling config file: %v", err) + os.Exit(1) + } + } + + // Create the server + var err error + ip := c.String(ipFlag.Name) + port := uint16(c.Uint(portFlag.Name)) + server, err := server.NewBeaconMockServer(logger, ip, port, config) + if err != nil { + fmt.Fprintf(os.Stderr, "Error creating server: %v", err) + os.Exit(1) + } + + // Start it + wg := &sync.WaitGroup{} + err = server.Start(wg) + if err != nil { + fmt.Fprintf(os.Stderr, "Error starting server: %v", err) + os.Exit(1) + } + port = server.GetPort() + + // Handle process closures + termListener := make(chan os.Signal, 1) + signal.Notify(termListener, os.Interrupt, syscall.SIGTERM) + go func() { + <-termListener + fmt.Println("Shutting down...") + err := server.Stop() + if err != nil { + fmt.Fprintf(os.Stderr, "Error stopping server: %v", err) + os.Exit(1) + } + }() + + // Run the daemon until closed + logger.Info(fmt.Sprintf("Started Beacon mock server on %s:%d", ip, port)) + wg.Wait() + fmt.Println("Server stopped.") + return nil + } + + // Run application + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/db/cfg_test.go b/db/cfg_test.go index a7715ff..885e677 100644 --- a/db/cfg_test.go +++ b/db/cfg_test.go @@ -3,14 +3,11 @@ package db import ( "testing" - "github.com/ethereum/go-ethereum/common" - "github.com/nodeset-org/beacon-mock/internal/test" "github.com/stretchr/testify/require" ) func TestConfigClone(t *testing.T) { - depositContract := common.HexToAddress(test.DepositContractAddressString) - c := NewConfig(depositContract, true) + c := NewDefaultConfig() clone := c.Clone() t.Log("Created config and clone") diff --git a/db/config.go b/db/config.go index 6e4e919..988d7d3 100644 --- a/db/config.go +++ b/db/config.go @@ -7,28 +7,13 @@ import ( ) const ( - DefaultChainID uint64 = 0x90de5e7 + DefaultChainID uint64 = 0x90de5e7 + DefaultDepositContractAddressString string = "0xde905175eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ) var ( // Default config - defaultConfig *Config = &Config{ - ChainID: DefaultChainID, - SecondsPerSlot: 12, - SlotsPerEpoch: 32, - EpochsPerSyncCommitteePeriod: 256, - GenesisTime: time.Now(), - GenesisForkVersion: []byte{0x00}, - GenesisValidatorsRoot: []byte{0x00}, - AltairForkVersion: common.FromHex("0x90de5e700"), - AltairForkEpoch: 0, - BellatrixForkVersion: common.FromHex("0x90de5e701"), - BellatrixForkEpoch: 0, - CapellaForkVersion: common.FromHex("0x90de5e702"), - CapellaForkEpoch: 0, - DenebForkVersion: common.FromHex("0x90de5e703"), - DenebForkEpoch: 0, - } + DefaultDepositContractAddress common.Address = common.HexToAddress(DefaultDepositContractAddressString) ) // Basic Beacon Chain configuration @@ -62,16 +47,27 @@ type Config struct { DenebForkEpoch uint64 } -// Creates a new config instance -func NewConfig(depositContract common.Address, useDefaults bool) *Config { - config := &Config{} - if !useDefaults { - return config +// Creates a new default config instance +func NewDefaultConfig() *Config { + defaultConfig := &Config{ + ChainID: DefaultChainID, + DepositContract: DefaultDepositContractAddress, + SecondsPerSlot: 12, + SlotsPerEpoch: 32, + EpochsPerSyncCommitteePeriod: 256, + GenesisTime: time.Now(), + GenesisForkVersion: []byte{0x00}, + GenesisValidatorsRoot: []byte{0x00}, + AltairForkVersion: common.FromHex("0x90de5e700"), + AltairForkEpoch: 0, + BellatrixForkVersion: common.FromHex("0x90de5e701"), + BellatrixForkEpoch: 0, + CapellaForkVersion: common.FromHex("0x90de5e702"), + CapellaForkEpoch: 0, + DenebForkVersion: common.FromHex("0x90de5e703"), + DenebForkEpoch: 0, } - - config = defaultConfig.Clone() - config.DepositContract = depositContract - return config + return defaultConfig } // Clones a config into a new instance diff --git a/go.mod b/go.mod index e6ca3dc..3590ab3 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,8 @@ require ( github.com/goccy/go-json v0.10.2 github.com/gorilla/mux v1.8.1 github.com/rocket-pool/node-manager-core v0.3.1-0.20240523031437-fea797b95b9c - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli/v2 v2.27.1 ) require ( @@ -18,6 +19,7 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect @@ -51,10 +53,13 @@ require ( github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 // indirect github.com/prysmaticlabs/gohashtree v0.0.4-beta // indirect github.com/prysmaticlabs/prysm/v5 v5.0.3 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sethvargo/go-password v0.2.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect github.com/tklauser/numcpus v0.7.0 // indirect @@ -63,6 +68,7 @@ require ( github.com/wealdtech/go-eth2-types/v2 v2.8.2 // indirect github.com/wealdtech/go-eth2-util v1.8.2 // indirect github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1 // indirect + github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/crypto v0.22.0 // indirect golang.org/x/mod v0.17.0 // indirect diff --git a/go.sum b/go.sum index b2823be..d613f9d 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,9 @@ github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= @@ -64,6 +67,9 @@ github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16M github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= @@ -77,6 +83,7 @@ github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= @@ -87,14 +94,29 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -116,8 +138,10 @@ github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZ github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -158,8 +182,19 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -184,8 +219,8 @@ github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294 h github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294/go.mod h1:ZVEbRdnMkGhp/pu35zq4SXxtvUwWK0J1MATtekZpH2Y= github.com/prysmaticlabs/prysm/v5 v5.0.3 h1:hUi0gu6v7aXmMQkl2GbrLoWcMhDNIbkVxRwrZchKbxU= github.com/prysmaticlabs/prysm/v5 v5.0.3/go.mod h1:v5Oz4A4cWljfxUmW7SDk/VBzoYnei+lzwJogvSqUZVs= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rocket-pool/node-manager-core v0.3.1-0.20240523031437-fea797b95b9c h1:zev2m0KaBVASAmBAZtGL8pI/VNM8T5N71HUVHYBduuw= github.com/rocket-pool/node-manager-core v0.3.1-0.20240523031437-fea797b95b9c/go.mod h1:Clii5aca9PvR4HoAlUs8dh2OsJbDDnJ4yL5EaQE1gSo= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= @@ -203,13 +238,15 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= @@ -236,29 +273,56 @@ github.com/wealdtech/go-eth2-wallet-types/v2 v2.11.0 h1:yX9+FfUXvPDvZ8Q5bhF+64AW github.com/wealdtech/go-eth2-wallet-types/v2 v2.11.0/go.mod h1:UVP9YFcnPiIzHqbmCMW3qrQ3TK5FOqr1fmKqNT9JGr8= github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -266,15 +330,28 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= @@ -285,15 +362,28 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/test/utils.go b/internal/test/utils.go index e318953..90cabf9 100644 --- a/internal/test/utils.go +++ b/internal/test/utils.go @@ -7,5 +7,4 @@ const ( Pubkey3String string = "0xbeac0903bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" WithdrawalCredentialsString string = "0xc12ed5eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" WithdrawalCredentials2String string = "0xc12ed52eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - DepositContractAddressString string = "0xde905175eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ) diff --git a/manager/manager.go b/manager/manager.go index d53f9b2..bd6a602 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -31,11 +31,6 @@ func NewBeaconMockManager(logger *slog.Logger, config *db.Config) *BeaconMockMan } } -// Create a new beacon mock manager instance using the default config -func NewBeaconMockManagerWithDefaultConfig(logger *slog.Logger, depositContract common.Address) *BeaconMockManager { - return NewBeaconMockManager(logger, db.NewConfig(depositContract, true)) -} - // Set the database for the manager directly if you need to custom provision it func (m *BeaconMockManager) SetDatabase(db *db.Database) { m.database = db diff --git a/server/get-validators_test.go b/server/get-validators_test.go index abe2795..1e6643e 100644 --- a/server/get-validators_test.go +++ b/server/get-validators_test.go @@ -153,7 +153,7 @@ func TestValidatorsByIndex_Post(t *testing.T) { strconv.FormatUint(uint64(id0), 10), v2.Pubkey.HexWithPrefix(), } - parsedResponse := getValidatorsResponse(t, ids) + parsedResponse := getValidatorsResponsePost(t, ids) // Make sure the response is correct require.Len(t, parsedResponse.Data, 2) diff --git a/server/server_test.go b/server/server_test.go index 0542965..ba837c2 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -8,7 +8,6 @@ import ( "sync" "testing" - "github.com/ethereum/go-ethereum/common" "github.com/nodeset-org/beacon-mock/db" "github.com/stretchr/testify/require" ) @@ -28,8 +27,7 @@ var ( // Initialize a common server used by all tests func TestMain(m *testing.M) { // Create the config - depositContract := common.HexToAddress(DepositContractAddressString) - config := db.NewConfig(depositContract, true) + config := db.NewDefaultConfig() // Create the server var err error From f23761ba4bac2b33570dadbc8fe47981de68e96c Mon Sep 17 00:00:00 2001 From: Joe Clapis Date: Thu, 23 May 2024 11:39:44 -0400 Subject: [PATCH 6/6] Bumped the linter timeout for CI --- .github/workflows/lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1481911..909dbf8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -34,6 +34,7 @@ jobs: # Optional: golangci-lint command line arguments. # args: --issues-exit-code=0 + args: --timeout=5m # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true