Skip to content

Commit

Permalink
feat: add bls signature verification
Browse files Browse the repository at this point in the history
  • Loading branch information
j75689 committed Jul 7, 2023
1 parent ef98eb2 commit 9b6f736
Show file tree
Hide file tree
Showing 20 changed files with 826 additions and 321 deletions.
567 changes: 357 additions & 210 deletions api/cosmos/staking/v1beta1/tx.pulsar.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions client/keys/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ The pass backend requires GnuPG: https://gnupg.org/
RenameKeyCommand(),
ParseKeyStringCommand(),
MigrateCommand(),
SignMsgKeysCmd(),
VerifySignatureCmd(),
)

cmd.PersistentFlags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
Expand Down
106 changes: 106 additions & 0 deletions client/keys/sign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package keys

import (
"encoding/hex"
"errors"

"github.com/cometbft/cometbft/crypto/tmhash"
"github.com/spf13/cobra"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
)

// SignMsgKeysCmd returns the Cobra Command for signing messages with the private key of a given name.
func SignMsgKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "sign [message]",
Short: "Sign message",
Long: "Return a signature from their associated name and address private key.",
RunE: runSignMsgCmd,
}

cmd.Flags().String(flags.FlagFrom, "", "Name or address of private key with which to sign")
return cmd
}

func runSignMsgCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("invalid number of arguments")
}

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

_, name, _, err := client.GetFromFields(clientCtx, clientCtx.Keyring, clientCtx.From)
if err != nil {
return err
}

msg, err := hex.DecodeString(args[0])
if err != nil {
return err
}
sig, _, err := clientCtx.Keyring.Sign(name, tmhash.Sum(msg))
if err != nil {
return err
}

cmd.Println(hex.EncodeToString(sig))
return nil
}

// VerifySignatureCmd returns the Cobra Command for verifying signatures with a given public key and message.
func VerifySignatureCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "verify [message] [signature]",
Short: "Verify signature",
Long: "Verify signature with public key and message",
RunE: runVerifySignatureCmd,
}

cmd.Flags().String(flags.FlagFrom, "", "Name or address of private key with which to sign")
return cmd
}

func runVerifySignatureCmd(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return errors.New("invalid number of arguments")
}

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

_, name, _, err := client.GetFromFields(clientCtx, clientCtx.Keyring, clientCtx.From)
if err != nil {
return err
}
record, err := clientCtx.Keyring.Key(name)
if err != nil {
return err
}

priv, err := record.ExtractPrivKey()
if err != nil {
return nil
}

msg, err := hex.DecodeString(args[0])
if err != nil {
return nil
}
signature, err := hex.DecodeString(args[1])
if err != nil {
return nil
}
if priv.PubKey().VerifySignature(tmhash.Sum(msg), signature) {
cmd.Println("Signature verify successfully")
} else {
cmd.Println("Signature verify failed")
}
return nil
}
5 changes: 5 additions & 0 deletions crypto/keyring/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ func (k *Record) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
return nil
}

// ExtractPrivKey returns the priv key
func (k *Record) ExtractPrivKey() (cryptotypes.PrivKey, error) {
return extractPrivKeyFromRecord(k)
}

func extractPrivKeyFromRecord(k *Record) (cryptotypes.PrivKey, error) {
rl := k.GetLocal()
if rl == nil {
Expand Down
6 changes: 5 additions & 1 deletion crypto/keys/eth/ethsecp256k1/pubkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ func (pubKey *PubKey) VerifySignature(msg, sig []byte) bool {
sig = sig[:len(sig)-1]
}

if len(msg) != crypto.DigestLength {
msg = crypto.Keccak256Hash(msg).Bytes()
}

// the signature needs to be in [R || S] format when provided to VerifySignature
return crypto.VerifySignature(pubKey.Key, crypto.Keccak256Hash(msg).Bytes(), sig)
return crypto.VerifySignature(pubKey.Key, msg, sig)
}
4 changes: 3 additions & 1 deletion proto/cosmos/staking/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ message MsgCreateValidator {
string relayer_address = 9 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string challenger_address = 10 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string bls_key = 11;
string bls_proof = 12;
}

// MsgCreateValidatorResponse defines the Msg/CreateValidator response type.
Expand Down Expand Up @@ -98,7 +99,8 @@ message MsgEditValidator {

string relayer_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string challenger_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string bls_key = 7; // The BLS pubkey for the authorized relayer/challenger
string bls_key = 7; // The BLS pubkey for the authorized relayer/challenger
string bls_proof = 8;
}

// MsgEditValidatorResponse defines the Msg/EditValidator response type.
Expand Down
14 changes: 14 additions & 0 deletions testutil/sims/address_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/eth/ethsecp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
Expand Down Expand Up @@ -144,6 +145,19 @@ func CreateTestPubKeys(numPubKeys int) []cryptotypes.PubKey {
return publicKeys
}

// CreateTestAccounts returns number of PubKey, PrivKey
func CreateTestAccounts(num int) ([]cryptotypes.PubKey, []cryptotypes.PrivKey) {
var publicKeys = make([]cryptotypes.PubKey, 0, num)
var privateKeys = make([]cryptotypes.PrivKey, 0, num)
for i := 0; i < num; i++ {
privKey, _ := ethsecp256k1.GenPrivKey()
publicKeys = append(publicKeys, privKey.PubKey())
privateKeys = append(privateKeys, privKey)
}

return publicKeys, privateKeys
}

// NewPubKeyFromHex returns a PubKey from a hex string.
func NewPubKeyFromHex(pk string) (res cryptotypes.PubKey) {
pkBytes, err := hex.DecodeString(pk)
Expand Down
11 changes: 9 additions & 2 deletions x/genutil/client/cli/gentx.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"path/filepath"

tmtypes "github.com/cometbft/cometbft/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/pkg/errors"
"github.com/spf13/cobra"

Expand All @@ -33,9 +34,9 @@ func GenTxCmd(mbm module.BasicManager, txEncCfg client.TxEncodingConfig, genBalI
fsCreateValidator, defaultsDesc := cli.CreateValidatorMsgFlagSet(ipDefault)

cmd := &cobra.Command{
Use: "gentx [key_name] [amount] [validator] [relayer] [challenger] [blskey]",
Use: "gentx [key_name] [amount] [validator] [relayer] [challenger] [blskey] [blsProof]",
Short: "Generate a genesis tx carrying a self delegation",
Args: cobra.ExactArgs(6),
Args: cobra.ExactArgs(7),
Long: fmt.Sprintf(`Generate a genesis transaction that creates a validator with a self-delegation,
that is signed by the key in the Keyring referenced by a given name. A node ID and Bech32 consensus
pubkey may optionally be provided. If they are omitted, they will be retrieved from the priv_validator.json
Expand All @@ -47,6 +48,7 @@ $ %s gentx my-key-name 1000000stake \
0x6D967dc83b625603c963713eABd5B43A281E595e \
0xcdd393723f1Af81faa3F3c87B51dAB72B6c68154 \
ac1e598ae0ccbeeaafa31bc6faefa85c2ae3138699cac79169cd718f1a38445201454ec092a86f200e08a15266bdc6e9 \
b68b819c2d431bd8ea800326bbcd91bbbbec5404b8f456b23c87de368c7f48507e7be120f32354ebf3df38c2b5808cebd4c07254f0b4626007c6d46fc05b260901 \
--home=/path/to/home/dir --keyring-backend=os --chain-id=greenfield_9000-1 \
--moniker="myvalidator" \
--commission-max-change-rate=0.01 \
Expand Down Expand Up @@ -172,12 +174,17 @@ $ %s gentx my-key-name 1000000stake \
if len(blsPk) != 2*sdk.BLSPubKeyLength {
return fmt.Errorf("invalid bls pubkey")
}
blsProof := args[6]
if len(blsProof) != 2*ethcrypto.SignatureLength {
return fmt.Errorf("invalid bls proof")
}

createValCfg.Validator = validator
createValCfg.Delegator = addr
createValCfg.Relayer = relayer
createValCfg.Challenger = challenger
createValCfg.BlsKey = blsPk
createValCfg.BLSProof = blsProof

// create a 'create-validator' message
txBldr, msg, err := cli.BuildCreateValidatorMsg(clientCtx, createValCfg, txFactory, true)
Expand Down
6 changes: 3 additions & 3 deletions x/slashing/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ func TestBeginBlocker(t *testing.T) {

ctx := app.BaseApp.NewContext(false, tmproto.Header{})

pks := simtestutil.CreateTestPubKeys(1)
pks, pvs := simtestutil.CreateTestAccounts(1)
simtestutil.AddTestAddrsFromPubKeys(bankKeeper, stakingKeeper, ctx, pks, stakingKeeper.TokensFromConsensusPower(ctx, 200))
addr, pk := sdk.AccAddress(pks[0].Address()), pks[0]
addr, pk, pv := sdk.AccAddress(pks[0].Address()), pks[0], pvs[0]
tstaking := stakingtestutil.NewHelper(t, ctx, stakingKeeper)

// bond the validator
power := int64(100)
amt := tstaking.CreateValidatorWithValPower(addr, pk, power, true)
amt := tstaking.CreateValidatorWithValPower(addr, pk, pv, power, true)
staking.EndBlocker(ctx, stakingKeeper)
require.Equal(
t, bankKeeper.GetAllBalances(ctx, addr),
Expand Down
6 changes: 5 additions & 1 deletion x/slashing/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/crypto/tmhash"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/prysmaticlabs/prysm/crypto/bls"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -79,11 +80,14 @@ func TestSlashingMsgs(t *testing.T) {
commission := stakingtypes.NewCommissionRates(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec())
blsSecretKey, _ := bls.RandKey()
blsPk := hex.EncodeToString(blsSecretKey.PublicKey().Marshal())
blsProofBuf, err := priv1.Sign(tmhash.Sum(blsSecretKey.PublicKey().Marshal()))
require.NoError(t, err)
blsProof := hex.EncodeToString(blsProofBuf)

createValidatorMsg, err := stakingtypes.NewMsgCreateValidator(
addr1, valKey.PubKey(),
bondCoin, description, commission, sdk.OneInt(),
addr1, addr1, addr1, addr1, blsPk,
addr1, addr1, addr1, addr1, blsPk, blsProof,
)
require.NoError(t, err)

Expand Down
2 changes: 2 additions & 0 deletions x/staking/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (
FlagAddressRelayer = "addr-relayer"
FlagAddressChallenger = "addr-challenger"
FlagBlsKey = "bls-key"
FlagBlsProof = "bls-proof"
)

// common flagsets to add to various functions
Expand Down Expand Up @@ -96,6 +97,7 @@ func FlagSetRelayerAddress() *flag.FlagSet {
func FlagSetBlsKey() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.String(FlagBlsKey, "", "The bls pubkey of the validator")
fs.String(FlagBlsProof, "", "The bls proof of the validator")
return fs
}

Expand Down
6 changes: 4 additions & 2 deletions x/staking/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,11 @@ func NewEditValidatorCmd() *cobra.Command {
}

blsPk, _ := cmd.Flags().GetString(FlagBlsKey)
blsProof, _ := cmd.Flags().GetString(FlagBlsProof)

msg := types.NewMsgEditValidator(
valAddr, description, newRate, newMinSelfDelegation,
relayer, challenger, blsPk,
relayer, challenger, blsPk, blsProof,
)

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
Expand Down Expand Up @@ -497,6 +498,7 @@ type TxCreateValidatorConfig struct {
Relayer sdk.AccAddress
Challenger sdk.AccAddress
BlsKey string
BLSProof string
}

func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, chainID string, valPubKey cryptotypes.PubKey) (TxCreateValidatorConfig, error) {
Expand Down Expand Up @@ -636,7 +638,7 @@ func BuildCreateValidatorMsg(clientCtx client.Context, config TxCreateValidatorC
msg, err := types.NewMsgCreateValidator(
config.Validator, config.PubKey,
amount, description, commissionRates, minSelfDelegation,
from, config.Delegator, config.Relayer, config.Challenger, config.BlsKey)
from, config.Delegator, config.Relayer, config.Challenger, config.BlsKey, config.BLSProof)
if err != nil {
return txBldr, msg, err
}
Expand Down
Loading

0 comments on commit 9b6f736

Please sign in to comment.