Skip to content
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

Merged
merged 3 commits into from
Jul 4, 2022

Conversation

gitferry
Copy link
Contributor

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.

Copy link
Member

@SebastianElvis SebastianElvis left a 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.

## 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`.
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

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.

Copy link
Contributor Author

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

Copy link

@fishermanymc fishermanymc Jun 29, 2022

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.

Copy link
Contributor Author

@gitferry gitferry Jun 30, 2022

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:

  1. m = BLS public key. We need to first sign m using the validator's Ed25519 private key to get A. Otherwise, anyone can claim ownership of the BLS private key. Then we need to sign A using the validator's BLS private key to get B.
  2. m= Ed25519 public key. We need to first sign m using the validator's BLS private key to get A. This binds BLS key and Ed25519 key. Then, that's it. We don't need to sign A 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?

Copy link
Contributor Author

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.


## Verification

To verify PoP:
Copy link
Member

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?

Copy link
Contributor Author

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.

# 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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"has already"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

Copy link
Contributor

@aakoshh aakoshh Jun 29, 2022

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.

Copy link
Contributor

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.

Copy link
Contributor Author

@gitferry gitferry Jun 30, 2022

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.

Copy link
Contributor

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.

## 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`.

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.

## Verification

To verify PoP:
1. Decrypt `B` using the BLS public key and get `A'`

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?

Copy link
Contributor Author

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!

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'`
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@gitferry
Copy link
Contributor Author

Many thanks for the review @aakoshh @SebastianElvis @fishermanymc. I have updated the spec according to our discussion.

Comment on lines 11 to 12
1. The `Staking` module processes `MsgCreateValidator` first as usual. If success, then
2. the `Checkpointing` module process `MsgCreateBlsKey`. If success, the registration succeeds.
Copy link
Contributor

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

  1. 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
  2. Delay the processing of the staking process to the end of the epoch, at which point it will happen as usual.

Copy link
Contributor Author

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)?

Copy link
Contributor

@aakoshh aakoshh Jun 30, 2022

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.

Copy link
Contributor Author

@gitferry gitferry Jun 30, 2022

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!


## Verification

To verify PoP, first we need to ensure that the BLS public key has never been registered. Then,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,

Comment on lines 34 to 38
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.
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice point. Thanks!

@gitferry gitferry force-pushed the gai/checkpointing-bls-key-registration branch from a76d2b0 to 8703766 Compare July 1, 2022 03:20
@gitferry
Copy link
Contributor Author

gitferry commented Jul 1, 2022

Thanks @aakoshh for the great discussions. Please see the updates.

Copy link
Contributor

@aakoshh aakoshh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 🙂

@gitferry gitferry merged commit 70c3c51 into main Jul 4, 2022
@gitferry gitferry deleted the gai/checkpointing-bls-key-registration branch July 4, 2022 02:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants