-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bank: Add function to replace empty account with upgradeable program on feature activation #32783
Bank: Add function to replace empty account with upgradeable program on feature activation #32783
Conversation
Codecov Report
@@ Coverage Diff @@
## master #32783 +/- ##
========================================
Coverage 81.7% 81.7%
========================================
Files 802 804 +2
Lines 217872 218079 +207
========================================
+ Hits 178144 178344 +200
- Misses 39728 39735 +7 |
hey @buffalojoec, you should get triage and than write access to the monorepo per https://github.com/solana-labs/contributor-access-policy so that CI will run on your PRs automatically. I'll vouch for ya and I'm pretty sure Jon will too |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I'm thinking through what would happen, I'm worried that this would break things unfortunately.
Let's imagine we have an upgradeable program on address A
, and we want to replace it with an upgradeable program on B
. Here's the initial situation:
A
's data isPDA(A)
PDA(A)
's data is the old programB
's data isPDA(B)
PDA(B)
's data is the new program
If we replace A
with B
, and PDA(A)
with PDA(B)
, then we get:
A
's data is justPDA(B)
<-- Uh oh!PDA(A)
's data is the new programB
is gonePDA(B)
is gone
So if we want to replace an upgradeable program with another, we only need to replace the account at PDA(A)
with PDA(B)
.
The feature-gate situation is a little bit strange though, since we want to create a whole new program at Feature111111
. I'm thinking you will need to replace A
with B
, but you'll also need to rewrite the data at A
to be PDA(A)
.
Let me know if this makes sense. Could you add some more to the test to reflect the data written into the accounts to check the situation that I'm worried about?
Yeah, I think that makes sense. In other words, replace the data account instead of the program account. I guess what's more confusing is how this even works to upgrade programs in the current state, without the data account being involved 🧐 Regardless, happy to revise and write some more testing ! |
Yes, and also rewrite the program account with the correct address if needed.
It only works with non-upgradeable programs, which just have the one account 😉 |
I just pushed a modified method and some fleshed out tests evaluating your points above @joncinque. Let me know if this aligns with what you were thinking. TL/DR: The I've added verbose comments in order to fuel discussion about how to handle each of the four cases identified in the changes. |
5291aae
to
d646a07
Compare
I wanted to create this updated comment to flesh out some of the logic articulated by d646a07. There are four scenarios where we want to consider how to handle program replacements, now that we're involving the data account as well. Each of these four scenarios has a corresponding test and is handled by some conditional block in the Definitions
ScenariosScenario 1: Program Account SwapThe existing program and the proposed new program both just have program accounts, no data accounts. Swap the existing program account for the new one.
Scenario 2: Program Account Swap and Create DataThe existing program does not have a data account, but the proposed new one does. Swap the existing program account for the PDA of the new data account, and create the new data account.
Scenario 3: Program Account Swap and Remove DataThe existing program has a data account, but the proposed new one does not. Swap the existing program account for the new program account, and remove the existing data account.
Scenario 4: Program Account SwapThe existing program and the proposed new program both have data accounts. Swap the existing data account for the new one, leaving the existing program account (with the PDA of the data account) unchanged.
Do these four scenarios and their articulated results make sense and sound safe? My initial thoughts:
|
d646a07
to
2463a40
Compare
After offline discussion with @joncinque I've removed Scenario 3 from the supported operations! |
2463a40
to
ea6b7dd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is on the right track, thanks for all the comments and tests. Now we just need to make sure it'll cover the feature gate program situation
Just pushed up some commits addressing your feedback. A few questions came to mind while working on them:
|
We just need to be sure that we're setting the owner that's in the new account, we don't need to check it necessarily. The issue to watch out for is swapping accounts that are on different loaders, and not setting the old account to the new program's loader.
We could simply read the data from the new program account and use that rather than every dealing with the new PDA, but it's up to you. Ignoring is probably fine.
Burn 'em! no need to have more lamports than necessary Side note: It's funny, I'm just realizing that scenario 3 is possible with loader v4, but we don't need to address it here. |
Looking through the code and all the cases, I'm starting to get cold feet about a generic replace function. This code is all really dangerous since it goes around normal consensus to move data and lamports around, so it should be as simple and limited as possible. All of the possible branches make me worry that we're going to miss a case and end up taking down the network because the capitalization is wrong. What do you think about keeping the refactor that you've done with I do appreciate all of the great work and all of the tests, but I think we should be as precise as possible to support only the cases that we need to. What do you think? |
Yeah it got sketchy real quick with all the different scenarios. Where is this function invoked anyway? I guess I didn't realize creating a new separate bank method was on the table |
You can see how it's used in #31764 in |
6ec0e55
to
afcfa55
Compare
This should be ready for another look. Let me know if I should squash and put up a new PR. No-op program:
Feature:
|
afcfa55
to
935aa1a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was so much easier to review, thanks a ton for reducing the scope!
I looked at the program you uploaded, and even though the extend program instruction is meant to go out with in 1.16, could you add some huge static string to make the size bigger? That way we can't get stuck with a program data account that's too small.
Also, is it a verifiable build? I'd like it to be possible for anyone in the multisig to check it to make sure it's correct before signing off on an upgrade.
56299f4
to
9143dad
Compare
Nice suggestions! Made things much cleaner. I also added another test! |
9143dad
to
4515791
Compare
4515791
to
91bfec7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the polish!
…on feature activation (#32783) * replace program account * modify for all cases * remove non-data swap * address tests & conditional feedback * get the rent involved * mix in owner & executable * feature-related cases * stripped back to feature-specific case only * added feature * address initial feedback * added more lamport checks * condense tests * using test_case * add fail cases to tests * more cleanup * add verifiably built program * update program account state * cleaned up serializing logic * use full word capitalization * rename old & new to dst & src * swap src and dst in parameters * add warnings and errors * rename feature to programify * test suite description clarity * remove strings from datapoints * spell out source and destination * more verbose comments in account replace functions * move lamport calculation * swap lamport check for state check * move replace functions to helper module * make replace_account methods fallible * refactor error handling * add test for source program state (cherry picked from commit 25460f7)
…rogram on feature activation (backport of #32783) (#33527) Bank: Add function to replace empty account with upgradeable program on feature activation (#32783) * replace program account * modify for all cases * remove non-data swap * address tests & conditional feedback * get the rent involved * mix in owner & executable * feature-related cases * stripped back to feature-specific case only * added feature * address initial feedback * added more lamport checks * condense tests * using test_case * add fail cases to tests * more cleanup * add verifiably built program * update program account state * cleaned up serializing logic * use full word capitalization * rename old & new to dst & src * swap src and dst in parameters * add warnings and errors * rename feature to programify * test suite description clarity * remove strings from datapoints * spell out source and destination * more verbose comments in account replace functions * move lamport calculation * swap lamport check for state check * move replace functions to helper module * make replace_account methods fallible * refactor error handling * add test for source program state (cherry picked from commit 25460f7) Co-authored-by: Joe C <[email protected]>
&feature::id(), | ||
&inline_feature_gate_program::noop_program::id(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these arguments swapped? My impression is that this code is trying to copy code from somewhere into the account with id of feature::id()
but this code uses feature_id()
as the source
instead of the destination
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They do appear to be swapped. Great catch, thanks! 😅
I personally don't think this approach is a good idea. I think the feature program should be added as a builtin program rather than an upgradeable program. The feature program is very sensitive and managing the upgrade authority for it sounds like a headache (speaking of, I'm guessing the upgrade authority keypair (4ktVfu5Kfw6kWYRhVjLfBhpEicgX8XekXLZgmooJm5Tu) is currently owned by @buffalojoec?). I think it's better to update the program using feature gates. That way we don't need to verify the bytecode every time it's upgraded.. and if the program is a builtin program, everyone can read the code easily. This approach also adds a lot of scary methods like I strongly think this change should be reverted. |
Some fair points for sure, thanks for weighing in! I've actually pulled the feature activation from the schedule in order to open up the conversation on this exact concern to a SIMD. We had similar feedback last week on the community call and in some of the SIMD 0072 reviews for the architecture this PR sets up a base for. I'd love to have some more discussion on your thoughts here in SIMD 0077, which corresponds to this change! |
I can weigh in later, but for now, I'd like these changes to be reverted due to the lack of test coverage for the new bank methods added here. Any objection to that? |
No, not from me! Seems like a good idea considering we have to wait for the SIMD to be approved now anyway. |
Yeah that's fair too, thank you! |
Problem
Currently,
replace_program_account
will only replace a program's account, but not the program's data account.In order to complete Step 1 of #32780, we need to be able to replace an empty account with an upgradeable program on feature activation.
Summary of Changes
This PR adds a function
replace_empty_account_with_upgradeable_program
to replace an empty account - such asFeature111111111
with a program account and a data account, effectively replacing this account with an upgradeable program.