diff --git a/Makefile b/Makefile index 6746e56e94a2..2234b9193c11 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,12 @@ ifeq (boltdb,$(findstring boltdb,$(COSMOS_BUILD_OPTIONS))) build_tags += boltdb endif +# handle blst +ifeq (blst,$(findstring blst,$(COSMOS_BUILD_OPTIONS))) + CGO_ENABLED=1 + build_tags += blst +endif + whitespace := whitespace += $(whitespace) comma := , diff --git a/crypto/codec/amino.go b/crypto/codec/amino.go index 2c83723a6d48..648637da276b 100644 --- a/crypto/codec/amino.go +++ b/crypto/codec/amino.go @@ -4,7 +4,7 @@ import ( "github.com/cometbft/cometbft/crypto/sr25519" "cosmossdk.io/core/legacy" - + bls12_381 "github.com/cosmos/cosmos-sdk/crypto/keys/bls12_381" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" @@ -21,6 +21,7 @@ func RegisterCrypto(cdc legacy.Amino) { ed25519.PubKeyName) cdc.RegisterConcrete(&secp256k1.PubKey{}, secp256k1.PubKeyName) + cdc.RegisterConcrete(&bls12_381.PubKey{}, bls12_381.PubKeyName) cdc.RegisterConcrete(&kmultisig.LegacyAminoPubKey{}, kmultisig.PubKeyAminoRoute) @@ -31,4 +32,5 @@ func RegisterCrypto(cdc legacy.Amino) { ed25519.PrivKeyName) cdc.RegisterConcrete(&secp256k1.PrivKey{}, secp256k1.PrivKeyName) + cdc.RegisterConcrete(&bls12_381.PrivKey{}, bls12_381.PrivKeyName) } diff --git a/crypto/codec/cmt.go b/crypto/codec/cmt.go index 3e62182870f3..9f6eccfdf88d 100644 --- a/crypto/codec/cmt.go +++ b/crypto/codec/cmt.go @@ -6,7 +6,7 @@ import ( "github.com/cometbft/cometbft/crypto/encoding" "cosmossdk.io/errors" - + bls12_381 "github.com/cosmos/cosmos-sdk/crypto/keys/bls12_381" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -24,6 +24,10 @@ func FromCmtProtoPublicKey(protoPk cmtprotocrypto.PublicKey) (cryptotypes.PubKey return &secp256k1.PubKey{ Key: protoPk.Secp256K1, }, nil + case *cmtprotocrypto.PublicKey_Bls12381: + return &bls12_381.PubKey{ + Key: protoPk.Bls12381, + }, nil default: return nil, errors.Wrapf(sdkerrors.ErrInvalidType, "cannot convert %v from Tendermint public key", protoPk) } @@ -44,6 +48,12 @@ func ToCmtProtoPublicKey(pk cryptotypes.PubKey) (cmtprotocrypto.PublicKey, error Secp256K1: pk.Key, }, }, nil + case *bls12_381.PubKey: + return cmtprotocrypto.PublicKey{ + Sum: &cmtprotocrypto.PublicKey_Bls12381{ + Bls12381: pk.Key, + }, + }, nil default: return cmtprotocrypto.PublicKey{}, errors.Wrapf(sdkerrors.ErrInvalidType, "cannot convert %v to Tendermint public key", pk) } diff --git a/crypto/codec/proto.go b/crypto/codec/proto.go index 89f4b508ed81..ffe6af31ec02 100644 --- a/crypto/codec/proto.go +++ b/crypto/codec/proto.go @@ -3,6 +3,7 @@ package codec import ( "cosmossdk.io/core/registry" + bls12_381 "github.com/cosmos/cosmos-sdk/crypto/keys/bls12_381" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" @@ -16,11 +17,13 @@ func RegisterInterfaces(registry registry.InterfaceRegistrar) { registry.RegisterInterface("cosmos.crypto.PubKey", pk) registry.RegisterImplementations(pk, &ed25519.PubKey{}) registry.RegisterImplementations(pk, &secp256k1.PubKey{}) + registry.RegisterImplementations(pk, &bls12_381.PubKey{}) registry.RegisterImplementations(pk, &multisig.LegacyAminoPubKey{}) var priv *cryptotypes.PrivKey registry.RegisterInterface("cosmos.crypto.PrivKey", priv) registry.RegisterImplementations(priv, &secp256k1.PrivKey{}) registry.RegisterImplementations(priv, &ed25519.PrivKey{}) + registry.RegisterImplementations(priv, &bls12_381.PrivKey{}) secp256r1.RegisterInterfaces(registry) } diff --git a/crypto/hd/algo.go b/crypto/hd/algo.go index d34bc6882390..32fb3af738fd 100644 --- a/crypto/hd/algo.go +++ b/crypto/hd/algo.go @@ -19,6 +19,9 @@ const ( // Ed25519Type represents the Ed25519Type signature system. // It is currently not supported for end-user keys (wallets/ledgers). Ed25519Type = PubKeyType("ed25519") + // Ed25519Type represents the Ed25519Type signature system. + // It is currently not supported for end-user keys (wallets/ledgers). + Bls12_381Type = PubKeyType("bls12_381") // Sr25519Type represents the Sr25519Type signature system. Sr25519Type = PubKeyType("sr25519") ) diff --git a/crypto/keys/bls12_381/const.go b/crypto/keys/bls12_381/const.go new file mode 100644 index 000000000000..bfabbdcc56dc --- /dev/null +++ b/crypto/keys/bls12_381/const.go @@ -0,0 +1,20 @@ +package bls12_381 + +const ( + PrivKeyName = "cometbft/PrivKeyBls12_381" + PubKeyName = "cometbft/PubKeyBls12_381" + // PubKeySize is the size, in bytes, of public keys as used in this package. + PubKeySize = 32 + // PrivKeySize is the size, in bytes, of private keys as used in this package. + PrivKeySize = 64 + // SignatureLength defines the byte length of a BLS signature. + SignatureLength = 96 + // SeedSize is the size, in bytes, of private key seeds. These are the + // private key representations used by RFC 8032. + SeedSize = 32 + + // MaxMsgLen defines the maximum length of the message bytes as passed to Sign. + MaxMsgLen = 32 + + KeyType = "bls12381" +) diff --git a/crypto/keys/bls12_381/key.go b/crypto/keys/bls12_381/key.go new file mode 100644 index 000000000000..70370ceb87b0 --- /dev/null +++ b/crypto/keys/bls12_381/key.go @@ -0,0 +1,131 @@ +package bls12_381 + +import ( + "bytes" + "errors" + "fmt" + + "github.com/cometbft/cometbft/crypto" + + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// =============================================================================================== +// Private Key +// =============================================================================================== + +// PrivKey is a wrapper around the Ethereum BLS12-381 private key type. This +// wrapper conforms to crypto.Pubkey to allow for the use of the Ethereum +// BLS12-381 private key type. + +var ( + _ cryptotypes.PrivKey = &PrivKey{} + _ codec.AminoMarshaler = &PrivKey{} +) + +// NewPrivateKeyFromBytes build a new key from the given bytes. +func NewPrivateKeyFromBytes(bz []byte) (PrivKey, error) { + panic("not implemented, build flags are required to use bls12_381 keys") +} + +// GenPrivKey generates a new key. +func GenPrivKey() (PrivKey, error) { + panic("not implemented, build flags are required to use bls12_381 keys") +} + +// Bytes returns the byte representation of the Key. +func (privKey PrivKey) Bytes() []byte { + panic("not implemented, build flags are required to use bls12_381 keys") +} + +// PubKey returns the private key's public key. If the privkey is not valid +// it returns a nil value. +func (privKey PrivKey) PubKey() cryptotypes.PubKey { + panic("not implemented, build flags are required to use bls12_381 keys") +} + +// Equals returns true if two keys are equal and false otherwise. +func (privKey PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool { + panic("not implemented, build flags are required to use bls12_381 keys") +} + +// Type returns the type. +func (PrivKey) Type() string { + return KeyType +} + +// Sign signs the given byte array. If msg is larger than +// MaxMsgLen, SHA256 sum will be signed instead of the raw bytes. +func (privKey PrivKey) Sign(msg []byte) ([]byte, error) { + panic("not implemented, build flags are required to use bls12_381 keys") +} + +// MarshalAmino overrides Amino binary marshaling. +func (privKey PrivKey) MarshalAmino() ([]byte, error) { + return privKey.Key, nil +} + +// UnmarshalAmino overrides Amino binary marshaling. +func (privKey *PrivKey) UnmarshalAmino(bz []byte) error { + if len(bz) != PrivKeySize { + return errors.New("invalid privkey size") + } + privKey.Key = bz + + return nil +} + +// MarshalAminoJSON overrides Amino JSON marshaling. +func (privKey PrivKey) MarshalAminoJSON() ([]byte, error) { + // When we marshal to Amino JSON, we don't marshal the "key" field itself, + // just its contents (i.e. the key bytes). + return privKey.MarshalAmino() +} + +// UnmarshalAminoJSON overrides Amino JSON marshaling. +func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error { + return privKey.UnmarshalAmino(bz) +} + +// =============================================================================================== +// Public Key +// =============================================================================================== + +// Pubkey is a wrapper around the Ethereum BLS12-381 public key type. This +// wrapper conforms to crypto.Pubkey to allow for the use of the Ethereum +// BLS12-381 public key type. + +var _ cryptotypes.PubKey = &PubKey{} + +// Address returns the address of the key. +// +// The function will panic if the public key is invalid. +func (pubKey PubKey) Address() crypto.Address { + panic("not implemented, build flags are required to use bls12_381 keys") +} + +// VerifySignature verifies the given signature. +func (pubKey PubKey) VerifySignature(msg, sig []byte) bool { + panic("not implemented, build flags are required to use bls12_381 keys") +} + +// Bytes returns the byte format. +func (pubKey PubKey) Bytes() []byte { + return pubKey.Key +} + +// Type returns the key's type. +func (PubKey) Type() string { + return KeyType +} + +// Equals returns true if the other's type is the same and their bytes are deeply equal. +func (pubKey PubKey) Equals(other cryptotypes.PubKey) bool { + return pubKey.Type() == other.Type() && bytes.Equal(pubKey.Bytes(), other.Bytes()) +} + +// String returns Hex representation of a pubkey with it's type +func (pubKey PubKey) String() string { + return fmt.Sprintf("PubKeyBLS12_381{%X}", pubKey.Key) +} diff --git a/crypto/keys/bls12_381/key_cgo.go b/crypto/keys/bls12_381/key_cgo.go new file mode 100644 index 000000000000..9ad681179dce --- /dev/null +++ b/crypto/keys/bls12_381/key_cgo.go @@ -0,0 +1,181 @@ +//go:build ((linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && bls12381 + +package bls12_381 + +import ( + "bytes" + "crypto/sha256" + "errors" + "fmt" + + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/tmhash" + + bls12381 "github.com/cosmos/crypto/curves/bls12381" + + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// =============================================================================================== +// Private Key +// =============================================================================================== + +// PrivKey is a wrapper around the Ethereum BLS12-381 private key type. This +// wrapper conforms to crypto.Pubkey to allow for the use of the Ethereum +// BLS12-381 private key type. + +var ( + _ cryptotypes.PrivKey = &PrivKey{} + _ codec.AminoMarshaler = &PrivKey{} +) + +// NewPrivateKeyFromBytes build a new key from the given bytes. +func NewPrivateKeyFromBytes(bz []byte) (PrivKey, error) { + secretKey, err := bls12381.SecretKeyFromBytes(bz) + if err != nil { + return PrivKey{}, err + } + return secretKey.Marshal(), nil +} + +// GenPrivKey generates a new key. +func GenPrivKey() (PrivKey, error) { + secretKey, err := bls12381.RandKey() + return PrivKey(secretKey.Marshal()), err +} + +// Bytes returns the byte representation of the Key. +func (privKey PrivKey) Bytes() []byte { + return privKey.Key +} + +// PubKey returns the private key's public key. If the privkey is not valid +// it returns a nil value. +func (privKey PrivKey) PubKey() cryptotypes.PubKey { + secretKey, err := bls12381.SecretKeyFromBytes(privKey.Key) + if err != nil { + return nil + } + + return PubKey(secretKey.PublicKey().Marshal()) +} + +// Equals returns true if two keys are equal and false otherwise. +func (privKey PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool { + return privKey.Type() == other.Type() && bytes.Equal(privKey.Bytes(), other.Bytes()) +} + +// Type returns the type. +func (PrivKey) Type() string { + return keyType +} + +// Sign signs the given byte array. If msg is larger than +// MaxMsgLen, SHA256 sum will be signed instead of the raw bytes. +func (privKey PrivKey) Sign(msg []byte) ([]byte, error) { + secretKey, err := bls12381.SecretKeyFromBytes(privKey.Key) + if err != nil { + return nil, err + } + + if len(msg) > MaxMsgLen { + hash := sha256.Sum256(msg) + sig := secretKey.Sign(hash[:]) + return sig.Marshal(), nil + } + sig := secretKey.Sign(msg) + return sig.Marshal(), nil +} + +// MarshalAmino overrides Amino binary marshaling. +func (privKey PrivKey) MarshalAmino() ([]byte, error) { + return privKey.Key, nil +} + +// UnmarshalAmino overrides Amino binary marshaling. +func (privKey *PrivKey) UnmarshalAmino(bz []byte) error { + if len(bz) != PrivKeySize { + return errors.New("invalid privkey size") + } + privKey.Key = bz + + return nil +} + +// MarshalAminoJSON overrides Amino JSON marshaling. +func (privKey PrivKey) MarshalAminoJSON() ([]byte, error) { + // When we marshal to Amino JSON, we don't marshal the "key" field itself, + // just its contents (i.e. the key bytes). + return privKey.MarshalAmino() +} + +// UnmarshalAminoJSON overrides Amino JSON marshaling. +func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error { + return privKey.UnmarshalAmino(bz) +} + +// =============================================================================================== +// Public Key +// =============================================================================================== + +// Pubkey is a wrapper around the Ethereum BLS12-381 public key type. This +// wrapper conforms to crypto.Pubkey to allow for the use of the Ethereum +// BLS12-381 public key type. + +var _ cryptotypes.PubKey = &PubKey{} + +// Address returns the address of the key. +// +// The function will panic if the public key is invalid. +func (pubKey PubKey) Address() crypto.Address { + pk, _ := bls12381.PublicKeyFromBytes(pubKey.Key) + if len(pk.Marshal()) != PubKeySize { + panic("pubkey is incorrect size") + } + return crypto.Address(tmhash.SumTruncated(pubKey.Key)) +} + +// VerifySignature verifies the given signature. +func (pubKey PubKey) VerifySignature(msg, sig []byte) bool { + if len(sig) != SignatureLength { + return false + } + + pubK, err := bls12381.PublicKeyFromBytes(pubKey.Key) + if err != nil { // invalid pubkey + return false + } + + if len(msg) > MaxMsgLen { + hash := sha256.Sum256(msg) + msg = hash[:] + } + + ok, err := bls12381.VerifySignature(sig, [MaxMsgLen]byte(msg[:MaxMsgLen]), pubK) + if err != nil { // bad signature + return false + } + + return ok +} + +// Bytes returns the byte format. +func (pubKey PubKey) Bytes() []byte { + return pubKey.Key +} + +// Type returns the key's type. +func (PubKey) Type() string { + return keyType +} + +// Equals returns true if the other's type is the same and their bytes are deeply equal. +func (pubKey PubKey) Equals(other cryptotypes.PubKey) bool { + return pubKey.Type() == other.Type() && bytes.Equal(pubKey.Bytes(), other.Bytes()) +} + +// String returns Hex representation of a pubkey with it's type +func (pubKey PubKey) String() string { + return fmt.Sprintf("PubKeyBLS12_381{%X}", pubKey.Key) +} diff --git a/crypto/keys/bls12_381/keys.pb.go b/crypto/keys/bls12_381/keys.pb.go new file mode 100644 index 000000000000..4524d9e5dd35 --- /dev/null +++ b/crypto/keys/bls12_381/keys.pb.go @@ -0,0 +1,504 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/crypto/bls12_381/keys.proto + +package bls12_381 + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// PubKey is an ed25519 public key for handling Tendermint keys in SDK. +// It's needed for Any serialization and SDK compatibility. +// It must not be used in a non Tendermint key context because it doesn't implement +// ADR-28. Nevertheless, you will like to use ed25519 in app user level +// then you must create a new proto message and follow ADR-28 for Address construction. +type PubKey struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *PubKey) Reset() { *m = PubKey{} } +func (*PubKey) ProtoMessage() {} +func (*PubKey) Descriptor() ([]byte, []int) { + return fileDescriptor_afa2b84d543bb80f, []int{0} +} +func (m *PubKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PubKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PubKey.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PubKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_PubKey.Merge(m, src) +} +func (m *PubKey) XXX_Size() int { + return m.Size() +} +func (m *PubKey) XXX_DiscardUnknown() { + xxx_messageInfo_PubKey.DiscardUnknown(m) +} + +var xxx_messageInfo_PubKey proto.InternalMessageInfo + +func (m *PubKey) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +// PrivKey defines a ed25519 private key. +// NOTE: ed25519 keys must not be used in SDK apps except in a tendermint validator context. +type PrivKey struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *PrivKey) Reset() { *m = PrivKey{} } +func (m *PrivKey) String() string { return proto.CompactTextString(m) } +func (*PrivKey) ProtoMessage() {} +func (*PrivKey) Descriptor() ([]byte, []int) { + return fileDescriptor_afa2b84d543bb80f, []int{1} +} +func (m *PrivKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PrivKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PrivKey.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PrivKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_PrivKey.Merge(m, src) +} +func (m *PrivKey) XXX_Size() int { + return m.Size() +} +func (m *PrivKey) XXX_DiscardUnknown() { + xxx_messageInfo_PrivKey.DiscardUnknown(m) +} + +var xxx_messageInfo_PrivKey proto.InternalMessageInfo + +func (m *PrivKey) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func init() { + proto.RegisterType((*PubKey)(nil), "cosmos.crypto.bls12_381.PubKey") + proto.RegisterType((*PrivKey)(nil), "cosmos.crypto.bls12_381.PrivKey") +} + +func init() { + proto.RegisterFile("cosmos/crypto/bls12_381/keys.proto", fileDescriptor_afa2b84d543bb80f) +} + +var fileDescriptor_afa2b84d543bb80f = []byte{ + // 244 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x4f, 0xca, 0x29, 0x36, 0x34, 0x8a, + 0x37, 0xb6, 0x30, 0xd4, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, + 0x87, 0xa8, 0xd1, 0x83, 0xa8, 0xd1, 0x83, 0xab, 0x91, 0x12, 0x4c, 0xcc, 0xcd, 0xcc, 0xcb, 0xd7, + 0x07, 0x93, 0x10, 0xb5, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, + 0x55, 0xf2, 0xe6, 0x62, 0x0b, 0x28, 0x4d, 0xf2, 0x4e, 0xad, 0x14, 0x12, 0xe0, 0x62, 0xce, 0x4e, + 0xad, 0x94, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x09, 0x02, 0x31, 0xad, 0xf4, 0x67, 0x2c, 0x90, 0x67, + 0xe8, 0x7a, 0xbe, 0x41, 0x4b, 0x22, 0x39, 0x3f, 0x37, 0xb5, 0x24, 0x29, 0xad, 0x44, 0x1f, 0xa2, + 0xd6, 0xc9, 0x27, 0x18, 0x62, 0xcf, 0xa4, 0xe7, 0x1b, 0xb4, 0x38, 0xb3, 0x53, 0x2b, 0xe3, 0xd3, + 0x32, 0x53, 0x73, 0x52, 0x94, 0x3c, 0xb9, 0xd8, 0x03, 0x8a, 0x32, 0xcb, 0xb0, 0x9b, 0xa6, 0x03, + 0x32, 0x49, 0x12, 0x61, 0x12, 0x44, 0x21, 0x0e, 0xa3, 0x9c, 0x7c, 0x4e, 0x3c, 0x92, 0x63, 0xbc, + 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, + 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x28, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, + 0x57, 0x1f, 0x16, 0x44, 0x60, 0x4a, 0xb7, 0x38, 0x25, 0x1b, 0x16, 0x5a, 0xa0, 0x30, 0x42, 0x04, + 0x59, 0x12, 0x1b, 0xd8, 0xb3, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc1, 0x05, 0x13, 0x8a, + 0x54, 0x01, 0x00, 0x00, +} + +func (m *PubKey) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PubKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PubKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PrivKey) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PrivKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PrivKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintKeys(dAtA []byte, offset int, v uint64) int { + offset -= sovKeys(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PubKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKeys(uint64(l)) + } + return n +} + +func (m *PrivKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKeys(uint64(l)) + } + return n +} + +func sovKeys(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozKeys(x uint64) (n int) { + return sovKeys(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PubKey) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PubKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PubKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKeys(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKeys + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PrivKey) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PrivKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PrivKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKeys(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKeys + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipKeys(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthKeys + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupKeys + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthKeys + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthKeys = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowKeys = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupKeys = fmt.Errorf("proto: unexpected end of group") +) diff --git a/crypto/keys/ed25519/ed25519.go b/crypto/keys/ed25519/ed25519.go index 8ded87ec3f77..792777b17de2 100644 --- a/crypto/keys/ed25519/ed25519.go +++ b/crypto/keys/ed25519/ed25519.go @@ -34,7 +34,7 @@ const ( // private key representations used by RFC 8032. SeedSize = 32 - keyType = "ed25519" + KeyType = "ed25519" ) var ( @@ -92,7 +92,7 @@ func (privKey *PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool { } func (privKey *PrivKey) Type() string { - return keyType + return KeyType } // MarshalAmino overrides Amino binary marshaling. @@ -193,7 +193,7 @@ func (pubKey *PubKey) String() string { } func (pubKey *PubKey) Type() string { - return keyType + return KeyType } func (pubKey *PubKey) Equals(other cryptotypes.PubKey) bool { diff --git a/crypto/keys/multisig/codec.go b/crypto/keys/multisig/codec.go index 6572123af540..47aa70060f7f 100644 --- a/crypto/keys/multisig/codec.go +++ b/crypto/keys/multisig/codec.go @@ -4,6 +4,7 @@ import ( "github.com/cometbft/cometbft/crypto/sr25519" "github.com/cosmos/cosmos-sdk/codec" + bls12_381 "github.com/cosmos/cosmos-sdk/crypto/keys/bls12_381" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -27,6 +28,8 @@ func init() { sr25519.PubKeyName) AminoCdc.RegisterConcrete(&secp256k1.PubKey{}, secp256k1.PubKeyName) + AminoCdc.RegisterConcrete(&bls12_381.PubKey{}, + bls12_381.PubKeyName) AminoCdc.RegisterConcrete(&LegacyAminoPubKey{}, PubKeyAminoRoute) } diff --git a/go.mod b/go.mod index 3ffd9f680f26..ae05108c4c4a 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/cosmos/btcutil v1.0.5 github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/cosmos-proto v1.0.0-beta.5 + github.com/cosmos/crypto v0.0.0-20240309083813-82ed2537802e github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogogateway v1.2.0 github.com/cosmos/gogoproto v1.5.0 @@ -85,7 +86,6 @@ require ( github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/cometbft/cometbft-db v0.12.0 // indirect - github.com/cosmos/crypto v0.0.0-20240309083813-82ed2537802e // indirect github.com/cosmos/iavl v1.1.4 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/danieljoos/wincred v1.2.1 // indirect diff --git a/simapp/simd/cmd/testnet.go b/simapp/simd/cmd/testnet.go index 56fffd41479c..5609fb1a2f20 100644 --- a/simapp/simd/cmd/testnet.go +++ b/simapp/simd/cmd/testnet.go @@ -292,7 +292,7 @@ func initTestnetFiles( } } - nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig) + nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig, args.algo) if err != nil { _ = os.RemoveAll(args.outputDir) return err diff --git a/testutil/network/network.go b/testutil/network/network.go index 3717f7e65b9d..da9159bd09ae 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -49,6 +49,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/runtime" srvconfig "github.com/cosmos/cosmos-sdk/server/config" @@ -446,7 +447,7 @@ func New(l Logger, baseDir string, cfg Config) (NetworkI, error) { mnemonic = cfg.Mnemonics[i] } - nodeID, pubKey, err := genutil.InitializeNodeValidatorFilesFromMnemonic(cmtCfg, mnemonic) + nodeID, pubKey, err := genutil.InitializeNodeValidatorFilesFromMnemonic(cmtCfg, mnemonic, ed25519.PrivKeyName) if err != nil { return nil, err } diff --git a/x/genutil/client/cli/collect.go b/x/genutil/client/cli/collect.go index f8e59533f10c..96b65f147e7d 100644 --- a/x/genutil/client/cli/collect.go +++ b/x/genutil/client/cli/collect.go @@ -26,7 +26,12 @@ func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, validator ty clientCtx := client.GetClientContextFromCmd(cmd) cdc := clientCtx.Codec - nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config) + consensusKey, err := cmd.Flags().GetString(FlagConsensusKeyAlgo) + if err != nil { + return errors.Wrap(err, "Failed to get consensus key algo") + } + + nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config, consensusKey) if err != nil { return errors.Wrap(err, "failed to initialize node validator files") } @@ -55,7 +60,7 @@ func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, validator ty return displayInfo(cmd.ErrOrStderr(), toPrint) }, } - + cmd.Flags().String(FlagConsensusKeyAlgo, "ed25519", "algorithm to use for the consensus key") cmd.Flags().String(flagGenTxDir, "", "override default \"gentx\" directory from which collect and execute genesis transactions; default [--home]/config/gentx/") return cmd diff --git a/x/genutil/client/cli/gentx.go b/x/genutil/client/cli/gentx.go index 9c6d0c5034c7..5b4b57f95ea5 100644 --- a/x/genutil/client/cli/gentx.go +++ b/x/genutil/client/cli/gentx.go @@ -62,7 +62,12 @@ $ %s gentx my-key-name 1000000stake --home=/path/to/home/dir --keyring-backend=o } cdc := clientCtx.Codec - nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config) + consensusKey, err := cmd.Flags().GetString(FlagConsensusKeyAlgo) + if err != nil { + return errors.Wrap(err, "Failed to get consensus key algo") + } + + nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config, consensusKey) if err != nil { return errors.Wrap(err, "failed to initialize node validator files") } @@ -212,6 +217,7 @@ $ %s gentx my-key-name 1000000stake --home=/path/to/home/dir --keyring-backend=o }, } + cmd.Flags().String(FlagConsensusKeyAlgo, "ed25519", "algorithm to use for the consensus key: ed25519 | secp256k1 | bls12_381 | sr25519 | multi") cmd.Flags().String(flags.FlagOutputDocument, "", "Write the genesis transaction JSON document to the given file instead of the default location") cmd.Flags().AddFlagSet(fsCreateValidator) flags.AddTxFlagsToCmd(cmd) diff --git a/x/genutil/client/cli/init.go b/x/genutil/client/cli/init.go index db9491df5997..19c3d148c48f 100644 --- a/x/genutil/client/cli/init.go +++ b/x/genutil/client/cli/init.go @@ -9,14 +9,10 @@ import ( "os" "path/filepath" - cfg "github.com/cometbft/cometbft/config" - cmttypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/go-bip39" - "github.com/spf13/cobra" - errorsmod "cosmossdk.io/errors" "cosmossdk.io/math/unsafe" - + cfg "github.com/cometbft/cometbft/config" + cmttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" @@ -25,6 +21,8 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/go-bip39" + "github.com/spf13/cobra" ) const ( @@ -113,7 +111,12 @@ func InitCmd(mm *module.Manager) *cobra.Command { initHeight = 1 } - nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic) + consensusKey, err := cmd.Flags().GetString(FlagConsensusKeyAlgo) + if err != nil { + return errorsmod.Wrap(err, "Failed to get consensus key algo") + } + + nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic, consensusKey) if err != nil { return err } @@ -163,10 +166,6 @@ func InitCmd(mm *module.Manager) *cobra.Command { Params: cmttypes.DefaultConsensusParams(), } - consensusKey, err := cmd.Flags().GetString(FlagConsensusKeyAlgo) - if err != nil { - return errorsmod.Wrap(err, "Failed to get consensus key algo") - } appGenesis.Consensus.Params.Validator.PubKeyTypes = []string{consensusKey} if err = genutil.ExportGenesisFile(appGenesis, genFile); err != nil { diff --git a/x/genutil/client/cli/init_test.go b/x/genutil/client/cli/init_test.go index b57bba44cd50..ba90403be880 100644 --- a/x/genutil/client/cli/init_test.go +++ b/x/genutil/client/cli/init_test.go @@ -22,6 +22,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/server" servercmtlog "github.com/cosmos/cosmos-sdk/server/log" "github.com/cosmos/cosmos-sdk/server/mock" @@ -243,7 +244,7 @@ func TestInitNodeValidatorFiles(t *testing.T) { cfg, err := genutiltest.CreateDefaultCometConfig(home) require.NoError(t, err) - nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(cfg) + nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(cfg, ed25519.KeyType) require.NoError(t, err) require.NotEqual(t, "", nodeID) diff --git a/x/genutil/utils.go b/x/genutil/utils.go index 880aa7bdc1c0..78fc18f85aed 100644 --- a/x/genutil/utils.go +++ b/x/genutil/utils.go @@ -8,6 +8,8 @@ import ( "time" cfg "github.com/cometbft/cometbft/config" + cmtcrypto "github.com/cometbft/cometbft/crypto" + cmtbls12381 "github.com/cometbft/cometbft/crypto/bls12381" tmed25519 "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/p2p" "github.com/cometbft/cometbft/privval" @@ -44,13 +46,13 @@ func ExportGenesisFileWithTime(genFile, chainID string, validators []cmttypes.Ge } // InitializeNodeValidatorFiles creates private validator and p2p configuration files. -func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey cryptotypes.PubKey, err error) { - return InitializeNodeValidatorFilesFromMnemonic(config, "") +func InitializeNodeValidatorFiles(config *cfg.Config, keyType string) (nodeID string, valPubKey cryptotypes.PubKey, err error) { + return InitializeNodeValidatorFilesFromMnemonic(config, "", keyType) } // InitializeNodeValidatorFilesFromMnemonic creates private validator and p2p configuration files using the given mnemonic. // If no valid mnemonic is given, a random one will be used instead. -func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic string) (nodeID string, valPubKey cryptotypes.PubKey, err error) { +func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic, keyType string) (nodeID string, valPubKey cryptotypes.PubKey, err error) { if len(mnemonic) > 0 && !bip39.IsMnemonicValid(mnemonic) { return "", nil, fmt.Errorf("invalid mnemonic") } @@ -71,15 +73,38 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin return "", nil, fmt.Errorf("could not create directory %q: %w", filepath.Dir(pvStateFile), err) } - var filePV *privval.FilePV + var ( + filePV *privval.FilePV + privKey cmtcrypto.PrivKey + ) + if len(mnemonic) == 0 { - filePV = privval.LoadOrGenFilePV(pvKeyFile, pvStateFile) + switch keyType { + case "ed25519": + privKey = tmed25519.GenPrivKey() + case "bls12_381": + privKey, err = cmtbls12381.GenPrivKey() + if err != nil { + return "", nil, err + } + default: + privKey = tmed25519.GenPrivKey() + } } else { - privKey := tmed25519.GenPrivKeyFromSecret([]byte(mnemonic)) - filePV = privval.NewFilePV(privKey, pvKeyFile, pvStateFile) - filePV.Save() + switch keyType { + case "ed25519": + privKey = tmed25519.GenPrivKeyFromSecret([]byte(mnemonic)) + case "bls12_381": + // TODO: need to add support for getting from mnemonic in Comet. + return "", nil, fmt.Errorf("BLS key type does not support mnemonic") + default: + privKey = tmed25519.GenPrivKeyFromSecret([]byte(mnemonic)) + } } + filePV = privval.NewFilePV(privKey, pvKeyFile, pvStateFile) + filePV.Save() + tmValPubKey, err := filePV.GetPubKey() if err != nil { return "", nil, err diff --git a/x/genutil/utils_test.go b/x/genutil/utils_test.go index 7e0dd4b17d4a..af2963df41fd 100644 --- a/x/genutil/utils_test.go +++ b/x/genutil/utils_test.go @@ -53,7 +53,7 @@ func TestInitializeNodeValidatorFilesFromMnemonic(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - _, _, err := InitializeNodeValidatorFilesFromMnemonic(cfg, tt.mnemonic) + _, _, err := InitializeNodeValidatorFilesFromMnemonic(cfg, tt.mnemonic, "ed25519") if tt.expError { require.Error(t, err)