-
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
cli: program upgrade with offline signing (--sign-only mode) #33860
Conversation
sdk/src/signer/mod.rs
Outdated
pub trait Signer { | ||
pub trait Signer: Debug { |
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.
Not sure what's the best approach in Rust to be able to println!(
a Trait at runtime, but this is the simplest one I got working, is it okay to keep Debug
constraint here (or should I remove it) ?
The downside - seems like I have to derive
Debug for each implementation (including test mocks), didn't find a way to do it in more re-usable manner.
The error compiler complains about looks like this:
error[E0521]: borrowed data escapes outside of function
--> cli/src/program.rs:1869:23
|
1858 | program_signers: Option<&[&dyn Signer]>,
| --------------- - let's call the lifetime of this reference `'1`
| |
| `program_signers` is a reference that is only valid in the function body
...
1869 | println!("signers {:?}", program_signers);
| ^^^^
| |
| `program_signers` escapes the function body here
| argument requires that `'1` must outlive `'static`
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
or
error[E0521]: borrowed data escapes outside of function
--> cli/src/program.rs:1869:23
|
1858 | program_signers: Option<&[&dyn Signer]>,
| --------------- - let's call the lifetime of this reference `'1`
| |
| `program_signers` is a reference that is only valid in the function body
...
1869 | println!("signers {:?}", &program_signers);
| ^^^^
| |
| `program_signers` escapes the function body here
| argument requires that `'1` must outlive `'static`
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
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.
Let's revert this change: Signer
has pubkey()
in its implementation, which is all that we should ever print anyway, so the previous implementation gives more flexibility.
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.
I used it to debug some things, wasn't sure whether it's better to leave it out or not,
but removed now (see latest PR version)
423bd08
to
2c57502
Compare
2c57502
to
b0c6879
Compare
Wrote some tests, and found rough edges ... maybe hold on proper review for now, I'll push another commit soon to address those. |
0ac8a3d
to
fd5af67
Compare
Finished tests, applied adjustments as needed and squashed everything into single commit, keeping PR in draft until all of these are added, but expecting to get feedback on the overall approach first, cc @joncinque |
clap-utils/src/keypair.rs
Outdated
/// loaded. If multiple equivalent (same pub key) signers are provided - only | ||
/// one of those will be returned in the result, such that NullSigner(s) | ||
/// always get lower priority. | ||
/// | ||
/// The returned value includes all of the `bulk_signers` that were not | ||
/// `None`, and maybe the default signer, if it was loaded. | ||
/// `None`, and maybe the default signer (if it was loaded). There is no | ||
/// guarantees on resulting signers ordering. |
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 needed to allow specifying same signer seamlessly multiple times (with some of those "duplicates" being NullSigner(s)). One case I found this to be helpful in is doing something like this:
solana program deploy --sign-only --fee-payer <OFFLINE_SIGNER_PUB_KEY> --program <PROGRAM_SIGNER_PUB_KEY> --upgrade-authority <OFFLINE_SIGNER> --buffer <BUFFER_PUB_KEY> --blockhash <VALUE> --max-len <VALUE> --min-rent-balance <VALUE>
where <OFFLINE_SIGNER>
can have same pubkey as <OFFLINE_SIGNER_PUB_KEY>
(and even perhaps <PROGRAM_SIGNER_PUB_KEY>
).
Also, I'm guessing ALL the changes I've introduced in clap-utils
must be copied 1-to-1 into clap-v3-utils
?
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.
Sorry, I'm not understanding the motivation for this. Why would someone specify the same signer different ways?
Unless I'm missing something, I think these changes should be in a separate PR so we can discuss there, since they don't seem to be needed for offline signing of program upgrades.
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.
Essentially, this is a prerequisite work for implementing this "refactoring": #33860 (comment)
One reason (closely-related to this PR) is the use-case of specifying both --upgrade-authority <OFFLINE_SIGNER_PUB_KEY>
and --signer <OFFLINE_SIGNER_SIGNATURE>
when doing solana program upgrade
:
solana program upgrade --fee-payer /Users/norwnd/.config/solana/id.json --program-id BgY6WizXe2Hfrq71TjjvV2ntjiMX3tjHKAARvg47SJUC --upgrade-authority GuKy3G22F4avoPKGtpqBEuoQYLEWaCRmncGGc5Q7JoPL --buffer C1o2XLFAU6EqZuMPVfW9C2gFEtBxMuXaNkEYLyNtsbiU --blockhash qyof8GDHGrVwUqtiDAFfit9MY62Xub2i1cEAnVXpQ62 --signer GuKy3G22F4avoPKGtpqBEuoQYLEWaCRmncGGc5Q7JoPL=Qonons7tm1oRxR2akT4cVu9HyXQjah2DxAzS8Ht7RwU3Gx1Nk2SUTv9X9LMa8mYFrPSRZbpYDsNVPG5BNNLGsh7
since OFFLINE_SIGNER_PUB_KEY
will convert to NullSigner
we need to differentiate between these 2 signers (preferring the most "complete" of these 2). Which is why I didn't create a separate PR for it - but can potentially move these changes into "a separate PR with preliminary changes" this PR depends on, if that helps with anything.
To sum up, I'm using the power of NullSigner
to avoid unnecessary if
s and duplicate code:
- I'm using
signer_of_or_null_signer
to treat pubkey arguments as any other signer (through NullSigner) generate_unique_signers
will choose the best signer for me (preferring real signers over NullSigner(s))fn process_program_deploy
,fn process_program_upgrade
will take only signer and work with it (corresponding pubkey, instead of being a 2nd "duplicate" argument, is abstracted away behind Signer interface)
Alternative way to address this (along the lines of what's done in current master
) would be to try parse --upgrade-authority
as both signer and pubkey in ("upgrade", Some(matches)) => {
and doing some if
-checks to decide which should be used, plus we'll need more if
-checks in fn process_program_deploy
and fn process_program_upgrade
(which I removed as mentioned here). Same for --fee-payer
.
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.
In the CLI, this is all done for you with signer_of
. If you follow the code to signer_from_path_with_config
, you'll see that if a pubkey is provided, the function will then check the --signer
args to see if a signature is provided, and use that signature for any transaction.
For example, see how process_transfer
is using the different signers:
Line 871 in 2c033e0
pub fn process_transfer( |
So the code is all in there for you already!
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.
Right, what you describe here solves it specifically for --signer
argument,
I've described here why we might want to solve it for other parameters too, might be a bit of a stretch on my part, wdyt ?
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.
Changed to get rid of this NullSigner-based approach I've suggested above (see latest PR version).
@norwnd , are you still working on this? Happy to re-open if so. |
Hi @CriesofCarrots! I'm happy to proceed if you or @joncinque can provide some feedback on this PR, it's in Draft because there are couple of minor things I still need to add, but it's ready for review otherwise (I'd rather wait for general-approach review first though). |
Hi @norwnd -- I sincerely apologize for taking such a long time to get to this. I really appreciate the effort that you've put into this. Looking at how many changes this requires, I'm leaning towards a slightly different way to implement it, through a totally new command called Currently, So rather than mess with |
Hey @joncinque, no worries,
I think we can move the upgrade functionality into a separate command to get cleaner result - I'll get to it in a day or two to try it out. Just want to note, Update: I just sketched out a "very rough diff" for introducing It doesn't quite address the overall complexity of |
d6eee19
to
72ad73f
Compare
Ah sorry, there was a misunderstanding. For The interface would be something like:
Or, in the enum, this would give:
Does that make more sense? |
63e6e02
to
54c34f4
Compare
54c34f4
to
d5c9aff
Compare
Squashed and rebased, proceeding to resolve other review-comments still left from previous reviews (I'll ping when it's ready for another look). |
d5c9aff
to
bcf977d
Compare
Hey @joncinque, I think I've addressed all the pending comments, pending your feedback now. Appreciate thorough reviews! |
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.
Looks really great! We're down to just tiny little changes, then this will be ready to merge. Thanks for all of your patience and edits
Oh also, it looks like the docs just got refactored, so you'll have to make the doc change in |
887683e
to
621297f
Compare
Thanks for the heads-up! This last push ^ was a rebase, it moved my stuff into appropriate file, but seems like there are path-dependent links that broke (I'll push a follow-up commit(s) to fix that). |
All the changes look great to me! There's a failure in the |
sdk/src/signer/mod.rs
Outdated
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.
Can you revert the changes here?
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.
Hm, that's creeped from a sloppy rebase, fixed in f3e466f
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## master #33860 +/- ##
=========================================
- Coverage 81.9% 81.8% -0.1%
=========================================
Files 819 819
Lines 220931 221019 +88
=========================================
- Hits 180954 180950 -4
- Misses 39977 40069 +92 |
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.
Problem
Resolves #23975.
Summary of Changes
This PR implements offline signing capabilities for
solana program upgrade
command (solana program-v4 deploy
is out of scope of this PR,solana program deploy
works as before and doesn't support offline signing) through--sign-only
and some helper flags. See rendered docs this PR adds for how it is supposed to work.Notable things:
--sign-only
mode), it could potentially be improved by serialising everything into 1 (or maybe 2-3) params - but I think it's better to leave this possible improvement out of this PR (and maybe consider adding toprogram-v4
later on)