Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: json rpc #22

Merged
merged 19 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions app/ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (

opchildante "github.com/initia-labs/OPinit/x/opchild/ante"
opchildtypes "github.com/initia-labs/OPinit/x/opchild/types"
initiaante "github.com/initia-labs/initia/app/ante"
evmkeeper "github.com/initia-labs/minievm/x/evm/keeper"

"github.com/skip-mev/block-sdk/v2/block"
auctionante "github.com/skip-mev/block-sdk/v2/x/auction/ante"
Expand All @@ -25,9 +27,11 @@ type HandlerOptions struct {
IBCkeeper *ibckeeper.Keeper
OPChildKeeper opchildtypes.AnteKeeper
AuctionKeeper auctionkeeper.Keeper
TxEncoder sdk.TxEncoder
MevLane auctionante.MEVLane
FreeLane block.Lane
EVMKeeper *evmkeeper.Keeper

TxEncoder sdk.TxEncoder
MevLane auctionante.MEVLane
FreeLane block.Lane
}

// NewAnteHandler returns an AnteHandler that checks and increments sequence
Expand All @@ -46,9 +50,13 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder")
}

if options.EVMKeeper == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "EVM keeper is required for ante builder")
}

sigGasConsumer := options.SigGasConsumer
if sigGasConsumer == nil {
sigGasConsumer = ante.DefaultSigVerificationGasConsumer
sigGasConsumer = initiaante.DefaultSigVerificationGasConsumer
}

txFeeChecker := options.TxFeeChecker
Expand Down Expand Up @@ -83,7 +91,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
ante.NewSetPubKeyDecorator(options.AccountKeeper),
ante.NewValidateSigCountDecorator(options.AccountKeeper),
ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer),
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
NewSigVerificationDecorator(options.AccountKeeper, options.EVMKeeper, options.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
ibcante.NewRedundantRelayDecorator(options.IBCkeeper),
auctionante.NewAuctionDecorator(options.AuctionKeeper, options.TxEncoder, options.MevLane),
Expand Down
200 changes: 200 additions & 0 deletions app/ante/sigverify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package ante

import (
"fmt"

"google.golang.org/protobuf/types/known/anypb"

errorsmod "cosmossdk.io/errors"
storetypes "cosmossdk.io/store/types"
txsigning "cosmossdk.io/x/tx/signing"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/initia-labs/initia/crypto/ethsecp256k1"
evmkeeper "github.com/initia-labs/minievm/x/evm/keeper"
)

// SigVerificationDecorator verifies all signatures for a tx and return an error if any are invalid. Note,
// the SigVerificationDecorator will not check signatures on ReCheck.
//
// CONTRACT: Pubkeys are set in context for all signers before this decorator runs
// CONTRACT: Tx must implement SigVerifiableTx interface
type SigVerificationDecorator struct {
ak authante.AccountKeeper
ek *evmkeeper.Keeper
signModeHandler *txsigning.HandlerMap
}

func NewSigVerificationDecorator(
ak authante.AccountKeeper,
ek *evmkeeper.Keeper,
signModeHandler *txsigning.HandlerMap) SigVerificationDecorator {
return SigVerificationDecorator{
ak: ak,
ek: ek,
signModeHandler: signModeHandler,
}
}

func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
sigTx, ok := tx.(authsigning.Tx)
if !ok {
return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")
}

// stdSigs contains the sequence number, account number, and signatures.
// When simulating, this would just be a 0-length slice.
sigs, err := sigTx.GetSignaturesV2()
if err != nil {
return ctx, err
}

signers, err := sigTx.GetSigners()
if err != nil {
return ctx, err
}

// check that signer length and signature length are the same
if len(sigs) != len(signers) {
return ctx, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signers), len(sigs))
}

for i, sig := range sigs {
acc, err := authante.GetSignerAcc(ctx, svd.ak, signers[i])
if err != nil {
return ctx, err
}

// retrieve pubkey
pubKey := acc.GetPubKey()
if !simulate && pubKey == nil {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set")
}

// Check account sequence number.
if sig.Sequence != acc.GetSequence() {
return ctx, errorsmod.Wrapf(
sdkerrors.ErrWrongSequence,
"account sequence mismatch, expected %d, got %d", acc.GetSequence(), sig.Sequence,
)
}

// retrieve signer data
genesis := ctx.BlockHeight() == 0
chainID := ctx.ChainID()
var accNum uint64
if !genesis {
accNum = acc.GetAccountNumber()
}

// no need to verify signatures on recheck tx
if !simulate && !ctx.IsReCheckTx() {
anyPk, _ := codectypes.NewAnyWithValue(pubKey)

signerData := txsigning.SignerData{
Address: acc.GetAddress().String(),
ChainID: chainID,
AccountNumber: accNum,
Sequence: acc.GetSequence(),
PubKey: &anypb.Any{
TypeUrl: anyPk.TypeUrl,
Value: anyPk.Value,
},
}
adaptableTx, ok := tx.(authsigning.V2AdaptableTx)
if !ok {
return ctx, fmt.Errorf("expected tx to implement V2AdaptableTx, got %T", tx)
}
txData := adaptableTx.GetSigningTxData()
err = verifySignature(ctx, pubKey, signerData, sig.Data, svd.signModeHandler, txData, svd.ek, tx)
if err != nil {
var errMsg string
if authante.OnlyLegacyAminoSigners(sig.Data) {
// If all signers are using SIGN_MODE_LEGACY_AMINO, we rely on VerifySignature to check account sequence number,
// and therefore communicate sequence number as a potential cause of error.
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d), sequence (%d) and chain-id (%s)", accNum, acc.GetSequence(), chainID)
} else {
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d) and chain-id (%s): (%s)", accNum, chainID, err.Error())
}
return ctx, errorsmod.Wrap(sdkerrors.ErrUnauthorized, errMsg)

}
}
}

return next(ctx, tx, simulate)
}

// defaultSigVerificationGasConsumer is the default implementation of SignatureVerificationGasConsumer. It consumes gas
// for signature verification based upon the public key type. The cost is fetched from the given params and is matched
// by the concrete type.
func DefaultSigVerificationGasConsumer(
meter storetypes.GasMeter, sig signing.SignatureV2, params types.Params,
) error {
pubkey := sig.PubKey
switch pubkey := pubkey.(type) {
case *ed25519.PubKey:
meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519")
return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "ED25519 public keys are unsupported")

case *secp256k1.PubKey, *ethsecp256k1.PubKey:
meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1")
return nil

case *secp256r1.PubKey:
meter.ConsumeGas(params.SigVerifyCostSecp256r1(), "ante verify: secp256r1")
return nil

case multisig.PubKey:
multisignature, ok := sig.Data.(*signing.MultiSignatureData)
if !ok {
return fmt.Errorf("expected %T, got, %T", &signing.MultiSignatureData{}, sig.Data)
}
err := consumeMultisignatureVerificationGas(meter, multisignature, pubkey, params, sig.Sequence)
if err != nil {
return err
}
return nil

default:
return errorsmod.Wrapf(sdkerrors.ErrInvalidPubKey, "unrecognized public key type: %T", pubkey)
}
}

// consumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature
func consumeMultisignatureVerificationGas(
meter storetypes.GasMeter, sig *signing.MultiSignatureData, pubkey multisig.PubKey,
params types.Params, accSeq uint64,
) error {
size := sig.BitArray.Count()
sigIndex := 0

for i := 0; i < size; i++ {
if !sig.BitArray.GetIndex(i) {
continue
}
sigV2 := signing.SignatureV2{
PubKey: pubkey.GetPubKeys()[i],
Data: sig.Signatures[sigIndex],
Sequence: accSeq,
}
err := DefaultSigVerificationGasConsumer(meter, sigV2, params)
if err != nil {
return err
}
sigIndex++
}

return nil
}
116 changes: 116 additions & 0 deletions app/ante/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package ante

import (
"context"
"fmt"

signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
errorsmod "cosmossdk.io/errors"
txsigning "cosmossdk.io/x/tx/signing"

cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/tx/signing"

evmkeeper "github.com/initia-labs/minievm/x/evm/keeper"
evmtypes "github.com/initia-labs/minievm/x/evm/types"

coretypes "github.com/ethereum/go-ethereum/core/types"
)

// internalSignModeToAPI converts a signing.SignMode to a protobuf SignMode.
func internalSignModeToAPI(mode signing.SignMode) (signingv1beta1.SignMode, error) {
switch mode {
case signing.SignMode_SIGN_MODE_DIRECT:
return signingv1beta1.SignMode_SIGN_MODE_DIRECT, nil
case signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON:
return signingv1beta1.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, nil
case signing.SignMode_SIGN_MODE_TEXTUAL:
return signingv1beta1.SignMode_SIGN_MODE_TEXTUAL, nil
case signing.SignMode_SIGN_MODE_DIRECT_AUX:
return signingv1beta1.SignMode_SIGN_MODE_DIRECT_AUX, nil
case signing.SignMode_SIGN_MODE_EIP_191:
return signingv1beta1.SignMode_SIGN_MODE_EIP_191, nil
default:
return signingv1beta1.SignMode_SIGN_MODE_UNSPECIFIED, fmt.Errorf("unsupported sign mode %s", mode)
}
}

// verifySignature verifies a transaction signature contained in SignatureData abstracting over different signing
// modes. It differs from verifySignature in that it uses the new txsigning.TxData interface in x/tx.
func verifySignature(
ctx context.Context,
pubKey cryptotypes.PubKey,
signerData txsigning.SignerData,
signatureData signing.SignatureData,
handler *txsigning.HandlerMap,
txData txsigning.TxData,
// required to verify EVM signatures
ek *evmkeeper.Keeper,
tx sdk.Tx,
) error {
switch data := signatureData.(type) {
case *signing.SingleSignatureData:
if data.SignMode == evmkeeper.SignMode_SIGN_MODE_ETHEREUM {
// eth sign mode
ethTx, expectedSender, err := evmkeeper.NewTxUtils(ek).ConvertCosmosTxToEthereumTx(ctx, tx)
if err != nil {
return err
}
if ethTx == nil {
return fmt.Errorf("failed to convert tx to ethereum tx")
}

sdkCtx := sdk.UnwrapSDKContext(ctx)
ethChainID := evmtypes.ConvertCosmosChainIDToEthereumChainID(sdkCtx.ChainID())
signer := coretypes.LatestSignerForChainID(ethChainID)
sender, err := signer.Sender(ethTx)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrorInvalidSigner, "failed to recover sender address: %v", err)
}

// check if the recovered sender matches the expected sender
if expectedSender == nil || *expectedSender != sender {
return errorsmod.Wrapf(sdkerrors.ErrorInvalidSigner, "expected sender %s, got %s", expectedSender, sender)
}

return nil
}

signMode, err := internalSignModeToAPI(data.SignMode)
if err != nil {
return err
}
signBytes, err := handler.GetSignBytes(ctx, signMode, signerData, txData)
if err != nil {
return err
}

if !pubKey.VerifySignature(signBytes, data.Signature) {
return fmt.Errorf("unable to verify single signer signature")
}

return nil

case *signing.MultiSignatureData:
multiPK, ok := pubKey.(multisig.PubKey)
if !ok {
return fmt.Errorf("expected %T, got %T", (multisig.PubKey)(nil), pubKey)
}
err := multiPK.VerifyMultisignature(func(mode signing.SignMode) ([]byte, error) {
signMode, err := internalSignModeToAPI(mode)
if err != nil {
return nil, err
}
return handler.GetSignBytes(ctx, signMode, signerData, txData)
}, data)
if err != nil {
return err
}
return nil
default:
return fmt.Errorf("unexpected SignatureData %T", signatureData)
}
}
Loading