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

Create PubKey proto types #7109

Closed
aaronc opened this issue Aug 19, 2020 · 20 comments · Fixed by #7284
Closed

Create PubKey proto types #7109

aaronc opened this issue Aug 19, 2020 · 20 comments · Fixed by #7284
Assignees
Labels
C:Keys Keybase, KMS and HSMs

Comments

@aaronc
Copy link
Member

aaronc commented Aug 19, 2020

Following up on #7008, we need to add .proto definitions of PubKey types to complete #6928 using Any.

My general proposal is to add proto types like this:

// proto/cosmos/crypto/secp256k1/secp256k1.proto

package cosmos.crypto.secp256k1;

message PubKey {
  bytes key = 1;
}

The big problem I see with this approach is that it would break amino marshaling which is needed for the keyring and JSON signing. I can think of two work-arounds

Approach 1: override MarshalAmino/MarshalJSON methods.

The downside to this is that overriding JSON affects proto JSON which might not be a good idea. But maybe it's okay...?

Approach 2: have separate amino and a proto types using struct embedding.

This would look like:

message PubKey {
  bytes key = 1 [(gogoproto.embed) = true, (gogoproto.casttype) = "PubKeyBytes"];
}

where PubKeyBytes (or whatever we call it) is the base type alias used for amino:

type PubKeyBytes []byte

I'm not in love with either approach but it's what we've got. Unless we want to abandon the Any approach and deal with this amino compatibility elsewhere... but that's also annoying.

Thoughts?

/cc @alexanderbez @sahith-narahari

@aaronc aaronc added the C:Keys Keybase, KMS and HSMs label Aug 19, 2020
@aaronc aaronc added this to the v0.40 [Stargate] milestone Aug 19, 2020
@alexanderbez
Copy link
Contributor

I think approach 2 seems to make the most sense tbh, although I don't quite follow what you mean by embedding. In general, I think it's favorable to be consistent, so that's why I like option 2.

@aaronc
Copy link
Member Author

aaronc commented Aug 19, 2020

By embedding I mean that the generated struct will look like:

type PubKey struct {
  PubKeyBytes
}

so that methods get forwarded to PubKeyBytes

@aaronc
Copy link
Member Author

aaronc commented Aug 19, 2020

In general I'm leaning towards approach 2 as well because I dislike having to deal with those overrides. They cause too many surprises.

@aaronc aaronc mentioned this issue Aug 19, 2020
9 tasks
@aaronc
Copy link
Member Author

aaronc commented Aug 19, 2020

I have a draft here #7110. I guess this approach could work. It is a bit messy with the Equals methods however...

@sahith-narahari
Copy link
Contributor

I too feel approach 2 is better, it's better to avoid overrides imo.

@aaronc
Copy link
Member Author

aaronc commented Aug 19, 2020

What do we do about Equals? Now we have two types for the same thing and Equals is broken.

@alexanderbez
Copy link
Contributor

Can we have a custom Equal method that compares types first?

@aaronc
Copy link
Member Author

aaronc commented Aug 19, 2020

Can we have a custom Equal method that compares types first?

Then the two types need to be aware of each other, or do something else entirely like performing equality with only Type() and Bytes().

@aaronc
Copy link
Member Author

aaronc commented Aug 19, 2020

Having separate proto and amino types is in general problematic because we still need to convert to/from amino JSON... 🤯

@aaronc
Copy link
Member Author

aaronc commented Aug 20, 2020

I'm kind of stuck on this... Not really sure what to do, in case anyone has any ideas.

@blushi blushi self-assigned this Aug 21, 2020
This was referenced Aug 27, 2020
@blushi
Copy link
Contributor

blushi commented Sep 3, 2020

Following up on #7102 (comment) and https://hackmd.io/1s1yKkjkQ7aER8uKnCc06Q?view, only secp256k1 pub keys have been migrated from tendermint to cosmos-sdk. In that regard, we are creating new PubKey proto types only for secp256k1 and existing multisig (PubKeyMultisigThreshold) pub key types as part of #7147 in order to be able to have custom Equals methods that can compare amino and proto types using Type() and Bytes() (cf. #7102 (comment))

While implementing tendermint crypto.PubKey methods for one of the new multisig proto types, I came across an issue with VerifySignature(msg []byte, sig []byte) bool which doesn't seem to be appropriate to handle MultiSignatureData in which signatures could use different SignMode's. In fact, it worked for the amino based PubKeyMultisigThreshold because it's expecting an AminoMultisignature assuming all signatures were made with SIGN_MODE_LEGACY_AMINO_JSON.

That being said, VerifySignature might not even be used with the new multisig proto types, in favor of VerifyMultisignature which takes a GetSignBytesFunc and then can handle multiple sign modes. So one short term (dirty) solution would be to have VerifySignature panic or always return false.

Another cleaner and long term solution would be to move away from tendermint's crypto.PubKey and have one internal to cosmos-sdk. This was also discussed as part of #5694 but this has been moved to 0.41 and might evolve a quite large refactoring. So it would be good to get further opinions on that.

/cc @amaurymartiny @robert-zaremba @anilcse @atheeshp @aaronc @clevinson @alexanderbez

@alexanderbez
Copy link
Contributor

Thanks for the excellent summary @blushi! Is there a way we can know to call VerifyMultisignature when the signature is a multi-sig and call VerifySignature otherwise?

@blushi
Copy link
Contributor

blushi commented Sep 4, 2020

@alexanderbez
Copy link
Contributor

Yes, MultisigThresholdPubKey#VerifySignature should panic!

@aaronc
Copy link
Member Author

aaronc commented Sep 9, 2020

The approach we have been working on so far is to have separate proto and amino types for secp256k1 and use conversion functions.

An alternative that we haven't considered is making some small upstream changes to go-amino that allows for separate MarshalAminoJSON and UnmarshalAminoJSON methods so that proto and amino JSON can be overridden separately. (go-amino already has Marshal/UnmarshalAmino methods for overriding binary.) This would allow us to not need separate proto and amino pubkey types and just override the amino marshaling methods.

In discussing this with @blushi and @amaurymartiny, we agreed that the first approach of separate proto and amino pubkey's has the downsides of:

  • more hackiness in the code base which will maintenance more complex going forward, and
  • potentially causing scope bloat causing problems in unexpected places like the key-ring or errors that only show up at runtime from using the wrong type.

So our preference now is to just make small changes to amino (here and here to support Marshal/UnmarshalAminoJSON. Our sense is that these changes will 1) be quite small for go-amino and 2) make maintainability simpler going forward because there will be a non-hacky way to do some special amino marshaling just for backwards compatibility.

@okwme we'll need to chat with you about how to make these upstream changes to that repo.

/cc @alexanderbez

@robert-zaremba
Copy link
Collaborator

@aaronc - do we need *Amino* infix? Isn't it enough to use the json.(Unm)Marshaler (MarshalJSON and UnmarshalJSON)?

@alexanderbez
Copy link
Contributor

ACK @aaronc -- In fact, I recall attempting to do this months ago when first starting to migrate to Proto, but abandoned it for reasons I can't recall. I'm sure you can still find my PR.

@robert-zaremba yes because when calling codec.MarshalJSON, we want amino to use the override where codec is either an amino or proto codec.

@aaronc
Copy link
Member Author

aaronc commented Sep 9, 2020

@robert-zaremba yes we need the Amino infix. Un/MarshalJSON is already used by amino. It is also used by jsonpb. This creates a problem as overriding Un/MarshalJSON will also affect proto JSON which we don't want. This change will allow us to cleanly isolate amino JSON serialization without affecting proto JSON.

@aaronc
Copy link
Member Author

aaronc commented Sep 9, 2020

@alexanderbez if you can find your PR that would be great. I'm not seeing anything.

@alexanderbez
Copy link
Contributor

I can't seem to find it either, but shouldn't be difficult to do again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C:Keys Keybase, KMS and HSMs
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants