Skip to content

Commit

Permalink
feat: introduce the Altai upgrade (#459)
Browse files Browse the repository at this point in the history
* feat: introduce the Altai upgrade

* fix test error

* fix CI issue
  • Loading branch information
pythonberg1997 committed Sep 19, 2024
1 parent 9398abd commit 0eeeb87
Show file tree
Hide file tree
Showing 16 changed files with 158 additions and 82 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ jobs:
**/go.sum
**/Makefile
Makefile
- name: install Compose
uses: ndeloof/install-compose-action@v0.0.1
with:
version: latest
legacy: true
- name: start localnet
if: env.GIT_DIFF
run: |
Expand Down
20 changes: 14 additions & 6 deletions client/tx/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import (
"os"
"strings"

"cosmossdk.io/math"
"github.com/spf13/pflag"

sdkmath "cosmossdk.io/math"
"github.com/cosmos/go-bip39"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
"github.com/spf13/pflag"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
Expand All @@ -24,7 +25,6 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
)

// Factory defines a client transaction factory that facilitates generating and
Expand Down Expand Up @@ -333,7 +333,7 @@ func (f Factory) BuildUnsignedTx(msgs ...sdk.Msg) (client.TxBuilder, error) {
return nil, errors.New("cannot provide both fees and gas prices")
}

glDec := math.LegacyNewDec(int64(f.gas))
glDec := sdkmath.LegacyNewDec(int64(f.gas))

// Derive the fees based on the provided gas prices, where
// fee = ceil(gasPrice * gasLimit).
Expand Down Expand Up @@ -434,7 +434,15 @@ func (f Factory) PrintEIP712MsgType(clientCtx client.Context, msgs ...sdk.Msg) e
if err != nil {
return fmt.Errorf("failed to get msg types: %s", err)
}
typedData, err := authtx.WrapTxToTypedData(chainID.Uint64(), signDoc, msgTypes)

typedDataDomain := apitypes.TypedDataDomain{
Name: "Greenfield Tx",
Version: "1.0.0",
ChainId: math.NewHexOrDecimal256(chainID.Int64()),
VerifyingContract: "0x71e835aff094655dEF897fbc85534186DbeaB75d",
Salt: "0",
}
typedData, err := authtx.WrapTxToTypedData(signDoc, msgTypes, typedDataDomain)
if err != nil {
return fmt.Errorf("failed to wrap tx to typedData: %s", err)
}
Expand Down
3 changes: 3 additions & 0 deletions types/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ const (

// Mongolian is the upgrade name for Mongolian upgrade
Mongolian = "Mongolian"

// Altai is the upgrade name for Altai upgrade
Altai = "Altai"
)
2 changes: 1 addition & 1 deletion x/auth/ante/sigverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul

// no need to verify signatures on recheck tx
if !simulate && !ctx.IsReCheckTx() {
err := authsigning.VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, tx, ctx.SigCache(), ctx.TxBytes())
err := authsigning.VerifySignature(ctx, pubKey, signerData, sig.Data, svd.signModeHandler, tx)
if err != nil {
errMsg := fmt.Sprintf("signature verification failed; please verify account (%s); err: %s", pubKey.Address(), err)
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg)
Expand Down
4 changes: 2 additions & 2 deletions x/auth/client/cli/tx_multisign.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) (err error) {
PubKey: sig.PubKey,
}

err = signing.VerifySignature(sig.PubKey, signingData, sig.Data, txCfg.SignModeHandler(), txBuilder.GetTx(), nil, nil)
err = signing.VerifySignature(sdk.Context{}, sig.PubKey, signingData, sig.Data, txCfg.SignModeHandler(), txBuilder.GetTx())
if err != nil {
addr, _ := sdk.AccAddressFromHexUnsafe(sig.PubKey.Address().String())
return fmt.Errorf("couldn't verify signature for address %s", addr)
Expand Down Expand Up @@ -329,7 +329,7 @@ func makeBatchMultisignCmd() func(cmd *cobra.Command, args []string) error {
}

for _, sig := range signatureBatch {
err = signing.VerifySignature(sig[i].PubKey, signingData, sig[i].Data, txCfg.SignModeHandler(), txBldr.GetTx(), nil, nil)
err = signing.VerifySignature(sdk.Context{}, sig[i].PubKey, signingData, sig[i].Data, txCfg.SignModeHandler(), txBldr.GetTx())
if err != nil {
return fmt.Errorf("couldn't verify signature: %w %v", err, sig)
}
Expand Down
2 changes: 1 addition & 1 deletion x/auth/client/cli/validate_sigs.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func printAndValidateSigs(
Sequence: accSeq,
PubKey: pubKey,
}
err = authsigning.VerifySignature(pubKey, signingData, sig.Data, signModeHandler, sigTx, nil, nil)
err = authsigning.VerifySignature(sdk.Context{}, pubKey, signingData, sig.Data, signModeHandler, sigTx)
if err != nil {
return false
}
Expand Down
5 changes: 5 additions & 0 deletions x/auth/migrations/legacytx/amino_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ func (stdTxSignModeHandler) GetSignBytes(mode signingtypes.SignMode, data signin
), nil
}

// GetSignBytesRuntime implements SignModeHandler.GetSignBytesRuntime
func (h stdTxSignModeHandler) GetSignBytesRuntime(ctx sdk.Context, mode signingtypes.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) {
return h.GetSignBytes(mode, data, tx)
}

// SignatureDataToAminoSignature converts a SignatureData to amino-encoded signature bytes.
// Only SIGN_MODE_LEGACY_AMINO_JSON is supported.
func SignatureDataToAminoSignature(cdc *codec.LegacyAmino, data signingtypes.SignatureData) ([]byte, error) {
Expand Down
14 changes: 11 additions & 3 deletions x/auth/signing/handler_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ package signing
import (
"fmt"

"github.com/cosmos/cosmos-sdk/types/tx/signing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
)

// SignModeHandlerMap is SignModeHandler that aggregates multiple SignModeHandler's into
Expand Down Expand Up @@ -50,11 +49,20 @@ func (h SignModeHandlerMap) Modes() []signing.SignMode {
return h.modes
}

// DefaultMode implements SignModeHandler.GetSignBytes
// GetSignBytes implements SignModeHandler.GetSignBytes
func (h SignModeHandlerMap) GetSignBytes(mode signing.SignMode, data SignerData, tx sdk.Tx) ([]byte, error) {
handler, found := h.signModeHandlers[mode]
if !found {
return nil, fmt.Errorf("can't verify sign mode %s", mode.String())
}
return handler.GetSignBytes(mode, data, tx)
}

// GetSignBytesRuntime implements SignModeHandler.GetSignBytesRuntime
func (h SignModeHandlerMap) GetSignBytesRuntime(ctx sdk.Context, mode signing.SignMode, data SignerData, tx sdk.Tx) ([]byte, error) {
handler, found := h.signModeHandlers[mode]
if !found {
return nil, fmt.Errorf("can't verify sign mode %s", mode.String())
}
return handler.GetSignBytesRuntime(ctx, mode, data, tx)
}
4 changes: 4 additions & 0 deletions x/auth/signing/sign_mode_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ type SignModeHandler interface {
// GetSignBytes returns the sign bytes for the provided SignMode, SignerData and Tx,
// or an error
GetSignBytes(mode signing.SignMode, data SignerData, tx sdk.Tx) ([]byte, error)

// GetSignBytesRuntime returns the sign bytes for the provided SignMode, SignerData and Tx,
// or an error based on the context information
GetSignBytesRuntime(ctx sdk.Context, mode signing.SignMode, data SignerData, tx sdk.Tx) ([]byte, error)
}

// SignerData is the specific information needed to sign a transaction that generally
Expand Down
100 changes: 57 additions & 43 deletions x/auth/signing/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
errorsmod "cosmossdk.io/errors"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
lru "github.com/hashicorp/golang-lru"

"github.com/cosmos/cosmos-sdk/crypto/keys/eth/ethsecp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
Expand All @@ -17,60 +16,30 @@ import (

// VerifySignature verifies a transaction signature contained in SignatureData abstracting over different signing modes
// and single vs multi-signatures.
func VerifySignature(pubKey cryptotypes.PubKey, signerData SignerData, sigData signing.SignatureData, handler SignModeHandler, tx sdk.Tx, sigCache *lru.ARCCache, txBytes []byte) error {
func VerifySignature(ctx sdk.Context, pubKey cryptotypes.PubKey, signerData SignerData, sigData signing.SignatureData, handler SignModeHandler, tx sdk.Tx) error {
switch data := sigData.(type) {
case *signing.SingleSignatureData:
// EIP712 signatures are verified in a different way
// In greenfield, we adapt another antehandler to reject non-EIP712 signatures
if data.SignMode == signing.SignMode_SIGN_MODE_EIP_712 {
// skip signature verification if we have a cache and the tx is already in it
if sigCache != nil && txBytes != nil {
if _, known := sigCache.Get(string(txBytes)); known {
return nil
}
}

sig := data.Signature

// check signature length
if len(sig) != ethcrypto.SignatureLength {
if len(data.Signature) != ethcrypto.SignatureLength {
return errorsmod.Wrap(sdkerrors.ErrorInvalidSigner, "signature length doesn't match typical [R||S||V] signature 65 bytes")
}

sigHash, err := handler.GetSignBytes(data.SignMode, signerData, tx)
if err != nil {
return err
}

// remove the recovery offset if needed (ie. Metamask eip712 signature)
if sig[ethcrypto.RecoveryIDOffset] == 27 || sig[ethcrypto.RecoveryIDOffset] == 28 {
sig[ethcrypto.RecoveryIDOffset] -= 27
}

// recover the pubkey from the signature
feePayerPubkey, err := secp256k1.RecoverPubkey(sigHash, sig)
if err != nil {
return errorsmod.Wrap(err, "failed to recover fee payer from sig")
}

ecPubKey, err := ethcrypto.UnmarshalPubkey(feePayerPubkey)
if err != nil {
return errorsmod.Wrap(err, "failed to unmarshal recovered fee payer pubkey")
}

// check that the recovered pubkey matches the one in the signerData data
pk := &ethsecp256k1.PubKey{
Key: ethcrypto.CompressPubkey(ecPubKey),
}
if !pubKey.Equals(pk) {
return errorsmod.Wrapf(sdkerrors.ErrorInvalidSigner, "feePayer's pubkey %s is different from signature's pubkey %s", pubKey, pk)
// skip signature verification if we have a cache and the tx is already in it
if ctx.SigCache() != nil && ctx.TxBytes() != nil {
if _, known := ctx.SigCache().Get(string(ctx.TxBytes())); known {
return nil
}
}

// add the tx to the cache if needed
if sigCache != nil && txBytes != nil {
sigCache.Add(string(txBytes), tx)
// verify signature
err := verifyEip712SignatureWithFallback(ctx, pubKey, data.Signature, handler, signerData, tx)
if err == nil && ctx.SigCache() != nil && ctx.TxBytes() != nil {
ctx.SigCache().Add(string(ctx.TxBytes()), tx)
}
return nil
return err
} else {
// original cosmos-sdk signature verification
signBytes, err := handler.GetSignBytes(data.SignMode, signerData, tx)
Expand All @@ -89,3 +58,48 @@ func VerifySignature(pubKey cryptotypes.PubKey, signerData SignerData, sigData s
return fmt.Errorf("unexpected SignatureData %T", sigData)
}
}

func verifyEip712SignatureWithFallback(ctx sdk.Context, pubKey cryptotypes.PubKey, sig []byte, handler SignModeHandler, signerData SignerData, tx sdk.Tx) error {
// try with the old sign scheme first (for backward compatibility)
sigHash, err := handler.GetSignBytes(signing.SignMode_SIGN_MODE_EIP_712, signerData, tx)
if err == nil {
if err := verifyEip712Signature(pubKey, sig, sigHash); err == nil {
return nil
}
}

// try with the new sign scheme
sigHash, err = handler.GetSignBytesRuntime(ctx, signing.SignMode_SIGN_MODE_EIP_712, signerData, tx)
if err != nil {
return err
}
return verifyEip712Signature(pubKey, sig, sigHash)
}

func verifyEip712Signature(pubKey cryptotypes.PubKey, sig []byte, msg []byte) error {
// remove the recovery offset if needed (ie. Metamask eip712 signature)
if sig[ethcrypto.RecoveryIDOffset] == 27 || sig[ethcrypto.RecoveryIDOffset] == 28 {
sig[ethcrypto.RecoveryIDOffset] -= 27
}

// recover the pubkey from the signature
feePayerPubkey, err := secp256k1.RecoverPubkey(msg, sig)
if err != nil {
return errorsmod.Wrap(err, "failed to recover fee payer from sig")
}

ecPubKey, err := ethcrypto.UnmarshalPubkey(feePayerPubkey)
if err != nil {
return errorsmod.Wrap(err, "failed to unmarshal recovered fee payer pubkey")
}

// check that the recovered pubkey matches the one in the signerData data
pk := &ethsecp256k1.PubKey{
Key: ethcrypto.CompressPubkey(ecPubKey),
}
if !pubKey.Equals(pk) {
return errorsmod.Wrapf(sdkerrors.ErrorInvalidSigner, "feePayer's pubkey %s is different from signature's pubkey %s", pubKey, pk)
}

return nil
}
2 changes: 1 addition & 1 deletion x/auth/signing/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,6 @@ func TestVerifySignature(t *testing.T) {
handler := MakeTestHandlerMap()
stdTx := legacytx.NewStdTx(msgs, fee, []legacytx.StdSignature{stdSig}, memo)
stdTx.TimeoutHeight = 10
err = signing.VerifySignature(pubKey, signerData, sigV2.Data, handler, stdTx, nil, nil)
err = signing.VerifySignature(sdk.Context{}, pubKey, signerData, sigV2.Data, handler, stdTx)
require.NoError(t, err)
}
8 changes: 6 additions & 2 deletions x/auth/tx/direct.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ package tx
import (
"fmt"

signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"

sdk "github.com/cosmos/cosmos-sdk/types"
types "github.com/cosmos/cosmos-sdk/types/tx"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
)

Expand Down Expand Up @@ -42,6 +41,11 @@ func (signModeDirectHandler) GetSignBytes(mode signingtypes.SignMode, data signi
return DirectSignBytes(bodyBz, authInfoBz, data.ChainID, data.AccountNumber)
}

// GetSignBytesRuntime implements SignModeHandler.GetSignBytesRuntime
func (h signModeDirectHandler) GetSignBytesRuntime(ctx sdk.Context, mode signingtypes.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) {
return h.GetSignBytes(mode, data, tx)
}

// DirectSignBytes returns the SIGN_MODE_DIRECT sign bytes for the provided TxBody bytes, AuthInfo bytes, chain ID,
// account number and sequence.
func DirectSignBytes(bodyBytes, authInfoBytes []byte, chainID string, accnum uint64) ([]byte, error) {
Expand Down
5 changes: 5 additions & 0 deletions x/auth/tx/direct_aux.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,8 @@ func (signModeDirectAuxHandler) GetSignBytes(

return signDocDirectAux.Marshal()
}

// GetSignBytesRuntime implements SignModeHandler.GetSignBytesRuntime
func (h signModeDirectAuxHandler) GetSignBytesRuntime(ctx sdk.Context, mode signingtypes.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) {
return h.GetSignBytes(mode, data, tx)
}
Loading

0 comments on commit 0eeeb87

Please sign in to comment.