-
Notifications
You must be signed in to change notification settings - Fork 170
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
doc: add BLS key registration spec #34
Conversation
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.
Great spec! I only have minor comments. The high-level idea can definitely work, but I guess there are many technical details that can only be shown in the code-level, e.g., new protobuf structs/messages for the hardcoded message and DB schema for the validator with BLS PK.
x/checkpointing/spec/registration.md
Outdated
## Proof of Possession | ||
|
||
To create a PoP associated with the BLS public key, the validator needs to do the following steps: | ||
1. It signs a predefined message `m`, e.g, "hello Babylon", using its Ed25519 private key and obtains a signature `A`. |
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.
It seems that the predefined message m
has to be hardcoded in genesis.proto
or params.proto
.
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 agree
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.
what about m
= BLS public key? Seems at least as secure as a hardcoded message.
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.
Using the Ed25519 public key also seems to work
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.
Yes. If using the Ed25519 public key as the message, then the message should be first signed using BLS and then Ed25519.
I don't think m= hello Babylon
would work: after seen A
, anyone will have A
and can then use its own BLS private key to sign A
to ruin the correct binding between Ed25519 and BLS. For example, when a malicious validator node see a BLS reigestration transaction, it can extract "A" and sign it using its own BLS, and then broadcast its own BLS registration transaction. When this node has better network, this fraud registration might be received by the network earlier than the legitimate one, which will yield a successful front-running attack.
Indeed, if m
is not a key-specific plaintext, then no matter it is Ed25519-then-BLS signed or BLS-then-Ed25519 signed, I feel the PoP is not secure.
Given that we will have to include the BLS public key in the transaction anyway, I recommend tha the plaintext is the BLS public key. This key is first Ed25519 signed, and the signature is then BLS signed.
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 don't think m= hello Babylon would work: after seen A, anyone will have A and can then use its own BLS private key to sign A to ruin the correct binding between Ed25519 and BLS. For example, when a malicious validator node see a BLS reigestration transaction, it can extract "A" and sign it using its own BLS, and then broadcast its own BLS registration transaction. When this node has better network, this fraud registration might be received by the network earlier than the legitimate one, which will yield a successful front-running attack.
Since we assume that each replica has been registered, why would a fixed m
break the binding between Ed25519 and BLS? You see, the address of the sender is bonded with its Ed25519 public key.
Given that we will have to include the BLS public key in the transaction anyway, I recommend that the plaintext is the BLS public key. This key is first Ed25519 signed, and the signature is then BLS signed.
Thanks for this suggestion. I think it is at least better than a fixed m
.
Let's compare two options:
m = BLS public key
. We need to first signm
using the validator's Ed25519 private key to getA
. Otherwise, anyone can claim ownership of the BLS private key. Then we need to signA
using the validator's BLS private key to get B.m= Ed25519 public key
. We need to first signm
using the validator's BLS private key to getA
. This binds BLS key and Ed25519 key. Then, that's it. We don't need to signA
using Ed25519 private key since the entire transaction will be signed by Ed25519 by default.
It seems that option 2 saves one more round than option 1. Does it make sense?
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.
Just had a discussion with @fishermanymc and realized that each transaction is signed by ECDSA. So, option 2 also needs a Ed25519 signature over A
.
The current decision is to go for option 2.
x/checkpointing/spec/registration.md
Outdated
|
||
## Verification | ||
|
||
To verify PoP: |
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.
Should PoP be stored somewhere on-chain or be discarded after the verification?
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.
It's a good question. Natively, it would be on-chain since it is a Tendermint transaction. But I don't think we need to store it in the module after the verification. In other words, we don't need to access it from outside.
x/checkpointing/spec/registration.md
Outdated
# Registration | ||
|
||
To participate in the checkpointing, a validator needs to register its BLS public key. | ||
We assume the validator is already registered its Ed25519 key to participate consensus. |
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.
"has 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.
Nice catch!
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.
According to the registration flow and the yet-to-be-defined wrapped version of the MsgCreateValidator
, this will happen in a single transaction.
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.
Or perhaps you mean the that the user sending the registration should already have an account, which would be associated with a key. I'm not exactly sure how registration works. The way I understand it is it has a validator key and an optional delegator key, and both have to sign the message. I don't see any signature in the message, though, so I assume "signing" here means signing the whole transaction.
That seems to suggest that one of these guys have to have an account and be able to pay the fees, right?
Can it be that the delegator has an account but the validator doesn't? Can I use my account to create a validator by the virtue of registration, delegating some coins to its stake? In that case the validator would not necessarily have to have a key up front.
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 mentioning this. Would it be possible that the MsgCreateBlsKey
(a new message type that will be defined in the checkpointing module) message to be included in the same transaction with the corresponding MsgCreateValidator
? That means the operation of creating a validator needs to first register an Ed25519 key and then register a BLS key. Separating the two steps into two messages does not affect the procedure of normal registration. And combining them into a single transaction ensures atomicity. Of course, the original MsgCreateValidator
needs to be processed first by the staking module, and then the MsgCreateBlsKey
processed by the checkpointing module.
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.
Yes, the two will be included in the same transaction, in fact the same message, by the virtue of wrapping.
The wrapped staking message (MsgCreateValidator
) will be delayed till the end of the epoch, because it would have an effect on the power distribution, but we can process the BLS part straight away and register the Ed25519<->BLS association at once, rejecting any further attempts to add any other pairing for either keys.
x/checkpointing/spec/registration.md
Outdated
## Proof of Possession | ||
|
||
To create a PoP associated with the BLS public key, the validator needs to do the following steps: | ||
1. It signs a predefined message `m`, e.g, "hello Babylon", using its Ed25519 private key and obtains a signature `A`. |
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.
what about m
= BLS public key? Seems at least as secure as a hardcoded message.
x/checkpointing/spec/registration.md
Outdated
## Verification | ||
|
||
To verify PoP: | ||
1. Decrypt `B` using the BLS public key and get `A'` |
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.
shouldn't it first check whether this Ed25519 has already been paired with a BLS?
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.
You are right. Each validator should only be associated with one BLS public key. I will add that check. Thanks!
x/checkpointing/spec/registration.md
Outdated
To verify PoP: | ||
1. Decrypt `B` using the BLS public key and get `A'` | ||
2. If `A'` != `A`, verification fails. Otherwise, | ||
3. Use the validator's Ed25519 public key to decrypt `A` and get `m'` |
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 would mention that this key must come from the signer of the transaction itself, not just the payload, to prevent anyone being able to steal the messages included in the transaction and pretend they have these keys.
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.
Thank for pointing it out. Yes, there would be a check to ensure that the Ed25519 public key must be the same as the one that the sender's last registration.
Many thanks for the review @aakoshh @SebastianElvis @fishermanymc. I have updated the spec according to our discussion. |
x/checkpointing/spec/registration.md
Outdated
1. The `Staking` module processes `MsgCreateValidator` first as usual. If success, then | ||
2. the `Checkpointing` module process `MsgCreateBlsKey`. If success, the registration succeeds. |
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'm not exactly sure what the staking module does, but we should
- Let the checkpointing module do the processing: check the PoP of both the Ed25519 and BLS keys, and register the association, rejecting the transaction if the BLS or the Ed25519 is already associated with some other key
- Delay the processing of the staking process to the end of the epoch, at which point it will happen as usual.
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 feel like we'd better not process MsgCreateBlsKey
ahead of the corresponding MsgCreateValidator
because the registration of the BLS key only makes sense after the validator is created.
Can the epoching
module wraps both MsgCreateValidator
and MsgCreateBlsKey
in a wrapper message like MsgWrappedRegistration
and delay the processing of the wrapper to the end of the epoch? During execution, the epoching
module uncovers the wrapper and delivers the two messages to the staking
module and the checkpointing
module, respectively (need to ensure `MsgCreateValidator is processed first)?
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.
During execution, the epoching module uncovers the wrapper and delivers the two messages to the staking module and the checkpointing module
I would rather not do that because it would mean the epoching module has to know about the existence of checkpointing, which it currently doesn't have to do.
the registration of the BLS key only makes sense after the validator is created.
I don't disagree, but at the same time, I think there's nothing wrong with registering the BLS-to-ECDSA association immediately. We basically put the validator into a "pre-registered" state where we know about their keys and they cannot deviate from those any more - any other transaction they send with a different BLS key, or any other validator trying to use the same BLS key, will fail immediately, rather than with a delay. It's much easier to learn the outcome now, than at the end of the epoch. See #32
The only way for the delayed transaction to fail is if the delegator doesn't have enough funds. They can just try again.
Can the epoching module wraps both MsgCreateValidator and MsgCreateBlsKey in a wrapper message
It's the checkpointing
module doing the wrapping, so epoching
can stay oblivious. It only offers its delay service for staking related stuff, except for the creation message.
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 see. It makes sense. Thanks @aakoshh!
x/checkpointing/spec/registration.md
Outdated
|
||
## Verification | ||
|
||
To verify PoP, first we need to ensure that the BLS public key has never been registered. Then, |
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.
To verify PoP, first we need to ensure that the BLS public key has never been registered. Then, | |
To verify PoP, first we need to ensure that the BLS public key has never been registered by a different validator, and that the current validator hasn't already registered a different BLS public key. Then, |
x/checkpointing/spec/registration.md
Outdated
1. if `m` != Ed25519 public key of the validator from `MsgCreateValidator`, verification fails. Otherwise, | ||
2. decrypt `B` using `m`. | ||
3. If `A'` != `A`, verification fails. Otherwise, | ||
4. Use the validator's BLS public key to decrypt `A` and get `m'`. | ||
5. If `m'` != `m`, verification fails. Otherwise, verification passes. |
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.
Just a note that A'
is not introduced.
I wonder if instead of saying that
PoP is composed of [
m
,A
,B
].
We could say that
PoP = encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = Ed25519_pk))
tx = sign(key = Ed25519_sk, msg = { BLS_pk, PoP })
and checking is
tx.signer.Ed25519_pk ?= decrypt(key = tx.signer.Ed25519_pk, data = decrypt(key = tx.msg.BLS_pk, data = tx.msg.PoP))
It's the same thing, but I find I have to jump up and down less to find what the letters mean.
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.
Nice point. Thanks!
a76d2b0
to
8703766
Compare
Thanks @aakoshh for the great discussions. Please see the updates. |
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 good! 🙂
This PR partly fixed BM-46.
This is a proposal for BLS key registration, which is critical to the safety of checkpointing.
I marked @aakoshh @SebastianElvis @fishermanymc as reviewers but comments from others are also welcome.