R4R: CheckTx AnteHandler for Ethereum Tx Messages (#505)
* Rename Ethereum tx message * Use new tx decoder in the ethermint app * Update ante handler to prevent spam/dos * Update ethereum msg signing/verification logic * Implement secp256k1 key types * Remove pointer from To method * Move sig check to after inartistic gas check * Add comment on chainID parsing * Updated validateIntrinsicGas godoc * Implement Fee method on eth tx msg * Add reference to spec for recoverEthSig * Upgrade TM to v0.27.0
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
package crypto
|
||||
|
||||
import "github.com/cosmos/cosmos-sdk/codec"
|
||||
|
||||
var cryptoCodec = codec.New()
|
||||
|
||||
func init() {
|
||||
RegisterCodec(cryptoCodec)
|
||||
}
|
||||
|
||||
// RegisterCodec registers all the necessary types with amino for the given
|
||||
// codec.
|
||||
func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterConcrete(PubKeySecp256k1{}, "crypto/PubKeySecp256k1", nil)
|
||||
cdc.RegisterConcrete(PrivKeySecp256k1{}, "crypto/PrivKeySecp256k1", nil)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
|
||||
secp256k1 "github.com/tendermint/btcd/btcec"
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
// PrivKeyToSecp256k1 accepts a Tendermint private key and attempts to convert
|
||||
// it to a SECP256k1 ecdsa.PrivateKey.
|
||||
func PrivKeyToSecp256k1(priv tmcrypto.PrivKey) (*ecdsa.PrivateKey, error) {
|
||||
secp256k1Key, ok := priv.(tmsecp256k1.PrivKeySecp256k1)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid private key type: %T", priv)
|
||||
}
|
||||
|
||||
ecdsaPrivKey, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), secp256k1Key[:])
|
||||
return ecdsaPrivKey.ToECDSA(), nil
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
secp256k1 "github.com/tendermint/btcd/btcec"
|
||||
tmed25519 "github.com/tendermint/tendermint/crypto/ed25519"
|
||||
tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
func TestPrivKeyToSecp256k1(t *testing.T) {
|
||||
// require valid SECP256k1 key to convert
|
||||
secp256k1PrivKey := tmsecp256k1.GenPrivKey()
|
||||
convertedPriv, err := PrivKeyToSecp256k1(secp256k1PrivKey)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, secp256k1PrivKey[:], (*secp256k1.PrivateKey)(convertedPriv).Serialize())
|
||||
|
||||
// require invalid ED25519 key not to convert
|
||||
ed25519PrivKey := tmed25519.GenPrivKey()
|
||||
convertedPriv, err = PrivKeyToSecp256k1(ed25519PrivKey)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, convertedPriv)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
ethsecp256k1 "github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// secp256k1 Private Key
|
||||
|
||||
var _ tmcrypto.PrivKey = PrivKeySecp256k1{}
|
||||
|
||||
// PrivKeySecp256k1 defines a type alias for an ecdsa.PrivateKey that implements
|
||||
// Tendermint's PrivateKey interface.
|
||||
type PrivKeySecp256k1 ecdsa.PrivateKey
|
||||
|
||||
// GenerateKey generates a new random private key. It returns an error upon
|
||||
// failure.
|
||||
func GenerateKey() (PrivKeySecp256k1, error) {
|
||||
priv, err := ethcrypto.GenerateKey()
|
||||
if err != nil {
|
||||
return PrivKeySecp256k1{}, err
|
||||
}
|
||||
|
||||
return PrivKeySecp256k1(*priv), nil
|
||||
}
|
||||
|
||||
// PubKey returns the ECDSA private key's public key.
|
||||
func (privkey PrivKeySecp256k1) PubKey() tmcrypto.PubKey {
|
||||
return PubKeySecp256k1{privkey.PublicKey}
|
||||
}
|
||||
|
||||
// Bytes returns the raw ECDSA private key bytes.
|
||||
func (privkey PrivKeySecp256k1) Bytes() []byte {
|
||||
return ethcrypto.FromECDSA(privkey.ToECDSA())
|
||||
}
|
||||
|
||||
// Sign creates a recoverable ECDSA signature on the secp256k1 curve over the
|
||||
// Keccak256 hash of the provided message. The produced signature is 65 bytes
|
||||
// where the last byte contains the recovery ID.
|
||||
func (privkey PrivKeySecp256k1) Sign(msg []byte) ([]byte, error) {
|
||||
return ethcrypto.Sign(ethcrypto.Keccak256Hash(msg).Bytes(), privkey.ToECDSA())
|
||||
}
|
||||
|
||||
// Equals returns true if two ECDSA private keys are equal and false otherwise.
|
||||
func (privkey PrivKeySecp256k1) Equals(other tmcrypto.PrivKey) bool {
|
||||
if other, ok := other.(PrivKeySecp256k1); ok {
|
||||
return bytes.Equal(privkey.Bytes(), other.Bytes())
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type.
|
||||
func (privkey PrivKeySecp256k1) ToECDSA() *ecdsa.PrivateKey {
|
||||
return (*ecdsa.PrivateKey)(&privkey)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// secp256k1 Public Key
|
||||
|
||||
var _ tmcrypto.PubKey = (*PubKeySecp256k1)(nil)
|
||||
|
||||
// PubKeySecp256k1 defines a type alias for an ecdsa.PublicKey that implements
|
||||
// Tendermint's PubKey interface.
|
||||
type PubKeySecp256k1 struct {
|
||||
pubkey ecdsa.PublicKey
|
||||
}
|
||||
|
||||
// Address returns the address of the ECDSA public key.
|
||||
func (key PubKeySecp256k1) Address() tmcrypto.Address {
|
||||
return tmcrypto.Address(ethcrypto.PubkeyToAddress(key.pubkey).Bytes())
|
||||
}
|
||||
|
||||
// Bytes returns the raw bytes of the ECDSA public key.
|
||||
func (key PubKeySecp256k1) Bytes() []byte {
|
||||
return ethcrypto.FromECDSAPub(&key.pubkey)
|
||||
}
|
||||
|
||||
// VerifyBytes 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.
|
||||
func (key PubKeySecp256k1) VerifyBytes(msg []byte, sig []byte) bool {
|
||||
if len(sig) == 65 {
|
||||
// remove recovery ID if contained in the signature
|
||||
sig = sig[:len(sig)-1]
|
||||
}
|
||||
|
||||
// the signature needs to be in [R || S] format when provided to VerifySignature
|
||||
return ethsecp256k1.VerifySignature(key.Bytes(), ethcrypto.Keccak256Hash(msg).Bytes(), sig)
|
||||
}
|
||||
|
||||
// Equals returns true if two ECDSA public keys are equal and false otherwise.
|
||||
func (key PubKeySecp256k1) Equals(other tmcrypto.PubKey) bool {
|
||||
if other, ok := other.(PubKeySecp256k1); ok {
|
||||
return bytes.Equal(key.Bytes(), other.Bytes())
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
ethsecp256k1 "github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/stretchr/testify/require"
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
func TestPrivKeySecp256k1PrivKey(t *testing.T) {
|
||||
// validate type and equality
|
||||
privKey, err := GenerateKey()
|
||||
require.NoError(t, err)
|
||||
require.True(t, privKey.Equals(privKey))
|
||||
require.Implements(t, (*tmcrypto.PrivKey)(nil), privKey)
|
||||
|
||||
// validate inequality
|
||||
privKey2, err := GenerateKey()
|
||||
require.NoError(t, err)
|
||||
require.False(t, privKey.Equals(privKey2))
|
||||
|
||||
// validate Ethereum address equality
|
||||
addr := privKey.PubKey().Address()
|
||||
expectedAddr := ethcrypto.PubkeyToAddress(privKey.PublicKey)
|
||||
require.Equal(t, expectedAddr.Bytes(), addr.Bytes())
|
||||
|
||||
// validate we can sign some bytes
|
||||
msg := []byte("hello world")
|
||||
sigHash := ethcrypto.Keccak256Hash(msg)
|
||||
expectedSig, _ := ethsecp256k1.Sign(sigHash.Bytes(), privKey.Bytes())
|
||||
|
||||
sig, err := privKey.Sign(msg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedSig, sig)
|
||||
}
|
||||
|
||||
func TestPrivKeySecp256k1PubKey(t *testing.T) {
|
||||
privKey, err := GenerateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
// validate type and equality
|
||||
pubKey := privKey.PubKey().(PubKeySecp256k1)
|
||||
require.Implements(t, (*tmcrypto.PubKey)(nil), pubKey)
|
||||
|
||||
// validate inequality
|
||||
privKey2, err := GenerateKey()
|
||||
require.NoError(t, err)
|
||||
require.False(t, pubKey.Equals(privKey2.PubKey()))
|
||||
|
||||
// validate signature
|
||||
msg := []byte("hello world")
|
||||
sig, err := privKey.Sign(msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Println("SIG LENGTH:", len(sig))
|
||||
res := pubKey.VerifyBytes(msg, sig)
|
||||
require.True(t, res)
|
||||
}
|
||||
Reference in New Issue
Block a user