Conversation
fefe7e4 to
126d992
Compare
5064109 to
659e14e
Compare
bobbinth
left a comment
There was a problem hiding this comment.
Looks good! Thank you! Not a full review, but I left some comments/questions inline.
| [signer] | ||
| Insecure = "/tmp/insecure_2" |
There was a problem hiding this comment.
This is for specifying validator private key, right? If so, I'd change the section to validator and allow specifying either private key or public key - e.g.,
[validator]
private_key = "hex-encoded-private-key"
# or
[validator]
public_key = "hex-encoded-public-key"Then, for dev/test scenarios, the users would be able to specify the private key, and for production scenarios, we could set the public key. Setting both at the same time would be an error.
Also, we should document this (I think we have a section that describes how the genesis file is to be used.
Lastly, I'd move this section up to be before native_faucet.
There was a problem hiding this comment.
Providing the public key alone would be insufficient for performing bootstrap - an entire signer needs to be provided in order to sign the genesis block.
There was a problem hiding this comment.
Ah yes - good point! I think we can do something like:
[validator]
private_key = "hex-encoded-private-key"
# or, for production settings:
[validator]
public_key = "hex-encoded-public-key"
block_signature = "hex-encoded-block-signature"But we can start with just the private_key option for now, and add the production-specific option later.
There was a problem hiding this comment.
This approach also means you can no longer do a default bootstrap - you need to wire together the secret key from toml for bootstrap command and then into the subsequent start command via CLI flags
There was a problem hiding this comment.
I see - so, we can use the private key file for both - genesis block construction and for node startup, right? In this case, probably fine to keep it as a file.
Another option is to output the private key file as a part of genesis construction procedure (similar to how we output the account files).
There was a problem hiding this comment.
I think we've gotten lost by focusing on the toml config file. What about keeping the current definition of the toml file - as an (optional) way to configure the contents of the genesis block.
We then take in the private key via CLI separately. I would prefer this be in-line aka no key file - the caller can always use a file and echo it in via an arg. imo this is maximally flexible and alleviates us having to manage a file since the key can just be passed in via env var.
There was a problem hiding this comment.
Yea that makes sense. Will update
bin/node/src/commands/bundled.rs
Outdated
| long = "validator-secret-key-filepath", | ||
| value_name = "VALIDATOR_SECRET_KEY_FILEPATH" | ||
| )] | ||
| validator_secret_key_filepath: Option<PathBuf>, |
There was a problem hiding this comment.
Related to the previous comment, I'm not sure this needs to be a filepath. A secret key is just a 32-byte string that could be hex encoded. But maybe keeping it as a file simplifies things?
There was a problem hiding this comment.
Given that this is only intended for devnet/local testing I think taking it inline is good enough.
What we can do is give it a more specific name e.g.
validator.development.secret_key
There was a problem hiding this comment.
Have done this. Note that genesis TOML is no longer optional because you need to use the bootstrap key in subsequent node start command.
| pub fn into_state<S>( | ||
| self, | ||
| signer: S, | ||
| ) -> Result<(GenesisState<S>, AccountSecrets), GenesisConfigError> { |
There was a problem hiding this comment.
Why do we need to parametrize this with the signer?
There was a problem hiding this comment.
GenesisState<S>::into_block() performs signing
Mirko-von-Leipzig
left a comment
There was a problem hiding this comment.
Thanks, looks good in general. The main contention is whether we truly need to be generic over the signer and whether we need a secret key file at all.
| pub fn bootstrap<S: BlockSigner>( | ||
| genesis: GenesisState<S>, |
There was a problem hiding this comment.
Do we not have a concrete implementation for this?
There was a problem hiding this comment.
How would we support secure + insecure signers without this abstraction?
There was a problem hiding this comment.
Can we flash out different scenarios/steps we are trying to cover? For example, we have the dev/testing flow, this flow requires us to have the private key in 2 places:
- When generating the genesis block. Here we need the private key for two reasons:
- To generate the validator's public key that goes into the block header.
- To sign the resulting block.
- When starting up the bundled node. Here, we pass the private key to the validator so that it can sign the blocks produced by the block producer.
The production flow will probably be different from this:
- The genesis config file would be pre-populated with the public key of the validator and the signature over the block. Or maybe, we'd have an external component that generates the genesis block out of band (from the genesis config file), and then, the node bootstraps from the pre-built genesis block.
- When we start up the validator component, we'll need to provide the private key to it. There are probably a couple of options to do it:
- The private key could be provided via an environment variable (probably not very secure).
- There could be a separate signing services (though, the benefit of such a services is somewhat questionable because it would be doing just "dumb signing" and so, whoever controls the validator can get it to sign anything).
- Are there other options?
There was a problem hiding this comment.
When we start up the validator component, we'll need to provide the private key to it. There are probably a couple of options to do it:
- The private key could be provided via an environment variable (probably not very secure).
- There could be a separate signing services (though, the benefit of such a services is somewhat questionable because it would be doing just "dumb signing" and so, whoever controls the validator can get it to sign anything).
For the second point, the main thing is that the corresponding key would be retrieved securely from some remote backend.
The genesis config file would be pre-populated with the public key of the validator and the signature over the block. Or maybe, we'd have an external component that generates the genesis block out of band (from the genesis config file), and then, the node bootstraps from the pre-built genesis block.
The out-of-band process should be as secure as the validator's process of signing blocks. Which would be an argument for using the same/similar approach that the validator uses above for creating the genesis block.
Having said that, I'm happy to remove the Signer trait usage from the bootstrap stack. I don't think we should remove it from the validator/bundled stack, however.
There was a problem hiding this comment.
You can remove it either by using an enum to represent signer options, or taking in a dyn T. At some point in the node you'll need to pass in a concrete implementation so we will know what that is.
There was a problem hiding this comment.
LMK if we want to go with enum or dyn T. If we go with the former then I will update base to remove the pub trait BlockSigner { altogether.
There was a problem hiding this comment.
If it doesn't introduce more complexity, I'd probably go with dyn BockSinger approach. But keeping as is is probably fine too.
bin/node/src/commands/bundled.rs
Outdated
| long = "validator-secret-key-filepath", | ||
| value_name = "VALIDATOR_SECRET_KEY_FILEPATH" | ||
| )] | ||
| validator_secret_key_filepath: Option<PathBuf>, |
There was a problem hiding this comment.
Given that this is only intended for devnet/local testing I think taking it inline is good enough.
What we can do is give it a more specific name e.g.
validator.development.secret_key
f0db6e8 to
5c481e6
Compare
bobbinth
left a comment
There was a problem hiding this comment.
Looks good! Thank you! I left some comments inline - once these are addressed, we can merge.
One set of comments is about making insecure_secret_key a required parameter. But I wonder if instead, if the key is not provided, we can assume some default value (e.g., 32 bytes of alternating ones and zeros). This way, in dev/test environments, users won't need to bother about this key at all.
| pub fee_parameters: FeeParameters, | ||
| pub version: u32, | ||
| pub timestamp: u32, | ||
| pub block_signer: S, |
There was a problem hiding this comment.
It is still not clear to me if the genesis state needs to be generic over BlocSigner - but I guess it doesn't create too much complexity.
| pub fn bootstrap<S: BlockSigner>( | ||
| genesis: GenesisState<S>, |
There was a problem hiding this comment.
If it doesn't introduce more complexity, I'd probably go with dyn BockSinger approach. But keeping as is is probably fine too.
Context
The Validator provides an endpoint for signing blocks. This endpoint has so far been stubbed out and needs to be implemented.
The Validator and bootstrap commands need to be configured with ECDSA signers to sign blocks. For now we will use insecure, locally stored secret keys for development and testing while providing an abstraction to facilitate a secure backend for signing in the future.
Relates to #1316.
Changes
SignBlockRPC endpoint to return ECDSASignature.