Skip to content

Commit

Permalink
Merge branch 'update-vuong' of https://github.com/notional-labs/cosmo…
Browse files Browse the repository at this point in the history
…s-sdk into dig-cosmos-sdk
  • Loading branch information
faddat committed Jan 22, 2022
2 parents 8bb7a96 + f4a4ddb commit b7761d6
Show file tree
Hide file tree
Showing 39 changed files with 4,611 additions and 1,576 deletions.
2 changes: 2 additions & 0 deletions client/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const (
SignModeDirect = "direct"
// SignModeLegacyAminoJSON is the value of the --sign-mode flag for SIGN_MODE_LEGACY_AMINO_JSON
SignModeLegacyAminoJSON = "amino-json"
// SignModeEIP191LegacyJSON is the value of the --sign-mode flag for SIGN_MODE_EIP191_LEGACY_JSON
SignModeEIP191LegacyJSON = "eip191-json"
)

// List of CLI flags
Expand Down
2 changes: 2 additions & 0 deletions client/tx/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) Factory {
signMode = signing.SignMode_SIGN_MODE_DIRECT
case flags.SignModeLegacyAminoJSON:
signMode = signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
case flags.SignModeEIP191LegacyJSON:
signMode = signing.SignMode_SIGN_MODE_EIP191_LEGACY_JSON
}

accNum, _ := flagSet.GetUint64(flags.FlagAccountNumber)
Expand Down
7 changes: 7 additions & 0 deletions crypto/codec/amino.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/ethsecp256k1"
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"

cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)

Expand All @@ -30,4 +32,9 @@ func RegisterCrypto(cdc *codec.LegacyAmino) {
ed25519.PrivKeyName, nil)
cdc.RegisterConcrete(&secp256k1.PrivKey{},
secp256k1.PrivKeyName, nil)

cdc.RegisterConcrete(&ethsecp256k1.PubKey{},
ethsecp256k1.PubKeyName, nil)
cdc.RegisterConcrete(&ethsecp256k1.PrivKey{},
ethsecp256k1.PrivKeyName, nil)
}
2 changes: 2 additions & 0 deletions crypto/codec/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package codec
import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/ethsecp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"
Expand All @@ -16,5 +17,6 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
registry.RegisterImplementations(pk, &ed25519.PubKey{})
registry.RegisterImplementations(pk, &secp256k1.PubKey{})
registry.RegisterImplementations(pk, &multisig.LegacyAminoPubKey{})
registry.RegisterImplementations(pk, &ethsecp256k1.PubKey{})
secp256r1.RegisterInterfaces(registry)
}
71 changes: 70 additions & 1 deletion crypto/hd/algo.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import (

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/types"

"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/cosmos/cosmos-sdk/crypto/keys/ethsecp256k1"
)

// PubKeyType defines an algorithm to derive key-pairs which can be used for cryptographic signing.
Expand All @@ -20,11 +24,14 @@ const (
Ed25519Type = PubKeyType("ed25519")
// Sr25519Type represents the Sr25519Type signature system.
Sr25519Type = PubKeyType("sr25519")
// EthSecp256k1Type defines the ECDSA secp256k1 used on Ethereum
EthSecp256k1Type = PubKeyType("ethsecp256k1")
)

var (
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters.
Secp256k1 = secp256k1Algo{}
Secp256k1 = secp256k1Algo{}
EthSecp256k1 = ethSecp256k1Algo{}
)

type DeriveFn func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error)
Expand Down Expand Up @@ -69,3 +76,65 @@ func (s secp256k1Algo) Generate() GenerateFn {
return &secp256k1.PrivKey{Key: bzArr}
}
}

type ethSecp256k1Algo struct {
}

// Name returns eth_secp256k1
func (s ethSecp256k1Algo) Name() PubKeyType {
return EthSecp256k1Type
}

func (s ethSecp256k1Algo) Derive() DeriveFn {
return func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) {
// Generate a Bip32 HD wallet for the mnemonic and a user supplied password
seed := bip39.NewSeed(mnemonic, bip39Passphrase)
// Generate a new master node using the seed.
masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
if err != nil {
return nil, err
}
// This gives the path: m/44H
// acc44H, err := masterKey.(hdkeychain.HardenedKeyStart + 44)
acc44H, err := masterKey.Derive(hdkeychain.HardenedKeyStart + 44)
if err != nil {
return nil, err
}
// This gives the path: m/44H/60H
acc44H60H, err := acc44H.Derive(hdkeychain.HardenedKeyStart + 60)
if err != nil {
return nil, err
}
// This gives the path: m/44H/60H/0H
acc44H60H0H, err := acc44H60H.Derive(hdkeychain.HardenedKeyStart + 0)
if err != nil {
return nil, err
}
// This gives the path: m/44H/60H/0H/0
acc44H60H0H0, err := acc44H60H0H.Derive(0)
if err != nil {
return nil, err
}
// This gives the path: m/44H/60H/0H/0/0
acc44H60H0H00, err := acc44H60H0H0.Derive(0)
if err != nil {
return nil, err
}
btcecPrivKey, err := acc44H60H0H00.ECPrivKey()
if err != nil {
return nil, err
}
privateKey := btcecPrivKey.ToECDSA()
// path := "m/44H/60H/0H/0/0"
derivedKey := privateKey.D.Bytes()
return derivedKey, err
}
}

func (s ethSecp256k1Algo) Generate() GenerateFn {
return func(bz []byte) types.PrivKey {
var bzArr = make([]byte, ethsecp256k1.PrivKeySize)
copy(bzArr, bz)
return &ethsecp256k1.PrivKey{Key: bzArr}
}
}
1 change: 1 addition & 0 deletions crypto/hd/algo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ func TestDefaults(t *testing.T) {
require.Equal(t, hd.PubKeyType("secp256k1"), hd.Secp256k1Type)
require.Equal(t, hd.PubKeyType("ed25519"), hd.Ed25519Type)
require.Equal(t, hd.PubKeyType("sr25519"), hd.Sr25519Type)
require.Equal(t, hd.PubKeyType("eth_secp256k1"), hd.EthSecp256k1Type)
}
2 changes: 1 addition & 1 deletion crypto/keyring/keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ type keystore struct {
func newKeystore(kr keyring.Keyring, opts ...Option) keystore {
// Default options for keybase
options := Options{
SupportedAlgos: SigningAlgoList{hd.Secp256k1},
SupportedAlgos: SigningAlgoList{hd.Secp256k1, hd.EthSecp256k1},
SupportedAlgosLedger: SigningAlgoList{hd.Secp256k1},
}

Expand Down
206 changes: 206 additions & 0 deletions crypto/keys/ethsecp256k1/ethsecp256k1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package ethsecp256k1

import (
"bytes"
"crypto/ecdsa"
"crypto/subtle"
"fmt"

ethcrypto "github.com/ethereum/go-ethereum/crypto"

"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

tmcrypto "github.com/tendermint/tendermint/crypto"
)

const (
// PrivKeySize defines the size of the PrivKey bytes
PrivKeySize = 32
// PubKeySize defines the size of the PubKey bytes
PubKeySize = 33
// KeyType is the string constant for the Secp256k1 algorithm
KeyType = "ethsecp256k1"
)

// Amino encoding names
const (
// PrivKeyName defines the amino encoding name for the EthSecp256k1 private key
PrivKeyName = "ethermint/PrivKeyEthSecp256k1"
// PubKeyName defines the amino encoding name for the EthSecp256k1 public key
PubKeyName = "ethermint/PubKeyEthSecp256k1"
)

// ----------------------------------------------------------------------------
// secp256k1 Private Key

var (
_ cryptotypes.PrivKey = &PrivKey{}
_ codec.AminoMarshaler = &PrivKey{}
)

// GenerateKey generates a new random private key. It returns an error upon
// failure.
func GenerateKey() (*PrivKey, error) {
priv, err := ethcrypto.GenerateKey()
if err != nil {
return nil, err
}

return &PrivKey{
Key: ethcrypto.FromECDSA(priv),
}, nil
}

// Bytes returns the byte representation of the ECDSA Private Key.
func (privKey PrivKey) Bytes() []byte {
return privKey.Key
}

// PubKey returns the ECDSA private key's public key.
func (privKey PrivKey) PubKey() cryptotypes.PubKey {
ecdsaPrivKey := privKey.ToECDSA()
return &PubKey{
Key: ethcrypto.CompressPubkey(&ecdsaPrivKey.PublicKey),
}
}

// Equals returns true if two ECDSA private keys are equal and false otherwise.
func (privKey PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool {
return privKey.Type() == other.Type() && subtle.ConstantTimeCompare(privKey.Bytes(), other.Bytes()) == 1
}

// Type returns ethsecp256k1
func (privKey PrivKey) Type() string {
return KeyType
}

// MarshalAmino overrides Amino binary marshalling.
func (privKey PrivKey) MarshalAmino() ([]byte, error) {
return privKey.Key, nil
}

// UnmarshalAmino overrides Amino binary marshalling.
func (privKey *PrivKey) UnmarshalAmino(bz []byte) error {
if len(bz) != PrivKeySize {
return fmt.Errorf("invalid privkey size, expected %d got %d", PrivKeySize, len(bz))
}
privKey.Key = bz

return nil
}

// MarshalAminoJSON overrides Amino JSON marshalling.
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 marshalling.
func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error {
return privKey.UnmarshalAmino(bz)
}

// Sign creates a recoverable ECDSA signature on the secp256k1 curve over the
// provided hash of the message. The produced signature is 65 bytes
// where the last byte contains the recovery ID.
func (privKey PrivKey) Sign(digestBz []byte) ([]byte, error) {
if len(digestBz) != ethcrypto.DigestLength {
digestBz = ethcrypto.Keccak256Hash(digestBz).Bytes()
}

return ethcrypto.Sign(digestBz, privKey.ToECDSA())
}

// ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type.
// The function will panic if the private key is invalid.
func (privKey PrivKey) ToECDSA() *ecdsa.PrivateKey {
key, err := ethcrypto.ToECDSA(privKey.Bytes())
if err != nil {
panic(err)
}
return key
}

// ----------------------------------------------------------------------------
// secp256k1 Public Key

var (
_ cryptotypes.PubKey = &PubKey{}
_ codec.AminoMarshaler = &PubKey{}
)

// Address returns the address of the ECDSA public key.
// The function will panic if the public key is invalid.
func (pubKey PubKey) Address() tmcrypto.Address {
pubk, err := ethcrypto.DecompressPubkey(pubKey.Key)
if err != nil {
panic(err)
}

return tmcrypto.Address(append(ethcrypto.PubkeyToAddress(*pubk).Bytes(), 0))
}

// Bytes returns the raw bytes of the ECDSA public key.
func (pubKey PubKey) Bytes() []byte {
return pubKey.Key
}

// String implements the fmt.Stringer interface.
func (pubKey PubKey) String() string {
return fmt.Sprintf("EthPubKeySecp256k1{%X}", pubKey.Key)
}

// Type returns ethsecp256k1
func (pubKey PubKey) Type() string {
return KeyType
}

// Equals returns true if the pubkey 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())
}

// MarshalAmino overrides Amino binary marshalling.
func (pubKey PubKey) MarshalAmino() ([]byte, error) {
return pubKey.Key, nil
}

// UnmarshalAmino overrides Amino binary marshalling.
func (pubKey *PubKey) UnmarshalAmino(bz []byte) error {
if len(bz) != PubKeySize {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "invalid pubkey size, expected %d, got %d", PubKeySize, len(bz))
}
pubKey.Key = bz

return nil
}

// MarshalAminoJSON overrides Amino JSON marshalling.
func (pubKey PubKey) 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 pubKey.MarshalAmino()
}

// UnmarshalAminoJSON overrides Amino JSON marshalling.
func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error {
return pubKey.UnmarshalAmino(bz)
}

// VerifySignature verifies that the ECDSA public key created a given signature over
// the provided message. It will calculate the Keccak256 hash of the message
// prior to verification.
//
// CONTRACT: The signature should be in [R || S] format.
func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool {
if len(sig) == ethcrypto.SignatureLength {
// remove recovery ID (V) if contained in the signature
sig = sig[:len(sig)-1]
}

// the signature needs to be in [R || S] format when provided to VerifySignature
return ethcrypto.VerifySignature(pubKey.Key, ethcrypto.Keccak256Hash(msg).Bytes(), sig)
}
Loading

0 comments on commit b7761d6

Please sign in to comment.