diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e58c4bf..2f96b770 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ # Changelog -## Unreleased +## [v0.20.0-rc1] - 2022-11-02 ### State Machine Breaking @@ -58,7 +58,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (cli) [#1226](https://github.com/evmos/ethermint/pull/1226) Add custom app db backend flag. * (ante) [#1289](https://github.com/evmos/ethermint/pull/1289) Change the fallback tx priority mechanism to be based on gas price. * (test) [#1311](https://github.com/evmos/ethermint/pull/1311) Add integration test for the `rollback` cmd -* (ledger) [#1277](https://github.com/evmos/ethermint/pull/1277) Add Ledger preprocessing transaction hook for EIP-712-signed Cosmos payloads. * (rpc) [#1296](https://github.com/evmos/ethermint/pull/1296) Add RPC Backend unit tests. * (rpc) [#1352](https://github.com/evmos/ethermint/pull/1352) Make the grpc queries run concurrently, don't block the consensus state machine. * (cli) [#1360](https://github.com/evmos/ethermint/pull/1360) Introduce a new `grpc-only` flag, such that when enabled, will start the node in a query-only mode. Note, gRPC MUST be enabled with this flag. diff --git a/ethereum/eip712/preprocess.go b/ethereum/eip712/preprocess.go deleted file mode 100644 index d79fc8b2..00000000 --- a/ethereum/eip712/preprocess.go +++ /dev/null @@ -1,83 +0,0 @@ -package eip712 - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/client" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cosmoskr "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" - "github.com/evmos/ethermint/types" -) - -// PreprocessLedgerTx reformats Ledger-signed Cosmos transactions to match the fork expected by Ethermint -// by including the signature in a Web3Tx extension and sending a blank signature in the body. -func PreprocessLedgerTx(chainID string, keyType cosmoskr.KeyType, txBuilder client.TxBuilder) error { - // Only process Ledger transactions - if keyType != cosmoskr.TypeLedger { - return nil - } - - // Init extension builder to set Web3 extension - extensionBuilder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder) - if !ok { - return fmt.Errorf("cannot cast TxBuilder to ExtensionOptionsTxBuilder") - } - - // Get signatures from TxBuilder - sigs, err := txBuilder.GetTx().GetSignaturesV2() - if err != nil { - return fmt.Errorf("could not get signatures: %w", err) - } - - // Verify single-signer - if len(sigs) != 1 { - return fmt.Errorf("invalid number of signatures, expected 1 and got %v", len(sigs)) - } - - signature := sigs[0] - sigData, ok := signature.Data.(*signing.SingleSignatureData) - if !ok { - return fmt.Errorf("unexpected signature type, expected SingleSignatureData") - } - sigBytes := sigData.Signature - - // Parse Chain ID as big.Int - chainIDInt, err := types.ParseChainID(chainID) - if err != nil { - return fmt.Errorf("could not parse chain id: %w", err) - } - - // Add ExtensionOptionsWeb3Tx extension with signature - var option *codectypes.Any - option, err = codectypes.NewAnyWithValue(&types.ExtensionOptionsWeb3Tx{ - FeePayer: txBuilder.GetTx().FeePayer().String(), - TypedDataChainID: chainIDInt.Uint64(), - FeePayerSig: sigBytes, - }) - if err != nil { - return fmt.Errorf("could not set extension as any: %w", err) - } - - extensionBuilder.SetExtensionOptions(option) - - // Set blank signature with Amino Sign Type - // (Regardless of input signMode, Evmos requires Amino signature type for Ledger) - blankSig := signing.SingleSignatureData{ - SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, - Signature: nil, - } - sig := signing.SignatureV2{ - PubKey: signature.PubKey, - Data: &blankSig, - Sequence: signature.Sequence, - } - - err = txBuilder.SetSignatures(sig) - if err != nil { - return fmt.Errorf("unable to set signatures on payload: %w", err) - } - - return nil -} diff --git a/ethereum/eip712/preprocess_test.go b/ethereum/eip712/preprocess_test.go deleted file mode 100644 index db80d44e..00000000 --- a/ethereum/eip712/preprocess_test.go +++ /dev/null @@ -1,222 +0,0 @@ -package eip712_test - -import ( - "encoding/hex" - "strings" - "testing" - - "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/client" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - "github.com/cosmos/cosmos-sdk/x/auth/ante" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/evmos/ethermint/app" - "github.com/evmos/ethermint/encoding" - "github.com/evmos/ethermint/ethereum/eip712" - "github.com/evmos/ethermint/tests" - "github.com/evmos/ethermint/types" - evmtypes "github.com/evmos/ethermint/x/evm/types" - "github.com/stretchr/testify/require" -) - -// Testing Constants -var ( - chainId = "ethermint_9000-1" - ctx = client.Context{}.WithTxConfig( - encoding.MakeConfig(app.ModuleBasics).TxConfig, - ) -) -var feePayerAddress = "ethm17xpfvakm2amg962yls6f84z3kell8c5lthdzgl" - -type TestCaseStruct struct { - txBuilder client.TxBuilder - expectedFeePayer string - expectedGas uint64 - expectedFee math.Int - expectedMemo string - expectedMsg string - expectedSignatureBytes []byte -} - -func TestLedgerPreprocessing(t *testing.T) { - // Update bech32 prefix - sdk.GetConfig().SetBech32PrefixForAccount("ethm", "") - - testCases := []TestCaseStruct{ - createBasicTestCase(t), - createPopulatedTestCase(t), - } - - for _, tc := range testCases { - // Run pre-processing - err := eip712.PreprocessLedgerTx( - chainId, - keyring.TypeLedger, - tc.txBuilder, - ) - - require.NoError(t, err) - - // Verify Web3 extension matches expected - hasExtOptsTx, ok := tc.txBuilder.(ante.HasExtensionOptionsTx) - require.True(t, ok) - require.True(t, len(hasExtOptsTx.GetExtensionOptions()) == 1) - - expectedExt := types.ExtensionOptionsWeb3Tx{ - TypedDataChainID: 9000, - FeePayer: feePayerAddress, - FeePayerSig: tc.expectedSignatureBytes, - } - - expectedExtAny, err := codectypes.NewAnyWithValue(&expectedExt) - require.NoError(t, err) - - actualExtAny := hasExtOptsTx.GetExtensionOptions()[0] - require.Equal(t, expectedExtAny, actualExtAny) - - // Verify signature type matches expected - signatures, err := tc.txBuilder.GetTx().GetSignaturesV2() - require.NoError(t, err) - require.Equal(t, len(signatures), 1) - - txSig := signatures[0].Data.(*signing.SingleSignatureData) - require.Equal(t, txSig.SignMode, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) - - // Verify signature is blank - require.Equal(t, len(txSig.Signature), 0) - - // Verify tx fields are unchanged - tx := tc.txBuilder.GetTx() - - require.Equal(t, tx.FeePayer().String(), tc.expectedFeePayer) - require.Equal(t, tx.GetGas(), tc.expectedGas) - require.Equal(t, tx.GetFee().AmountOf(evmtypes.DefaultParams().EvmDenom), tc.expectedFee) - require.Equal(t, tx.GetMemo(), tc.expectedMemo) - - // Verify message is unchanged - if tc.expectedMsg != "" { - require.Equal(t, len(tx.GetMsgs()), 1) - require.Equal(t, tx.GetMsgs()[0].String(), tc.expectedMsg) - } else { - require.Equal(t, len(tx.GetMsgs()), 0) - } - } -} - -func TestBlankTxBuilder(t *testing.T) { - txBuilder := ctx.TxConfig.NewTxBuilder() - - err := eip712.PreprocessLedgerTx( - chainId, - keyring.TypeLedger, - txBuilder, - ) - - require.Error(t, err) -} - -func TestNonLedgerTxBuilder(t *testing.T) { - txBuilder := ctx.TxConfig.NewTxBuilder() - - err := eip712.PreprocessLedgerTx( - chainId, - keyring.TypeLocal, - txBuilder, - ) - - require.NoError(t, err) -} - -func TestInvalidChainId(t *testing.T) { - txBuilder := ctx.TxConfig.NewTxBuilder() - - err := eip712.PreprocessLedgerTx( - "invalid-chain-id", - keyring.TypeLedger, - txBuilder, - ) - - require.Error(t, err) -} - -func createBasicTestCase(t *testing.T) TestCaseStruct { - t.Helper() - txBuilder := ctx.TxConfig.NewTxBuilder() - - feePayer, err := sdk.AccAddressFromBech32(feePayerAddress) - require.NoError(t, err) - - txBuilder.SetFeePayer(feePayer) - - // Create signature unrelated to payload for testing - signatureHex := strings.Repeat("01", 65) - signatureBytes, err := hex.DecodeString(signatureHex) - require.NoError(t, err) - - _, privKey := tests.NewAddrKey() - sigsV2 := signing.SignatureV2{ - PubKey: privKey.PubKey(), // Use unrelated public key for testing - Data: &signing.SingleSignatureData{ - SignMode: signing.SignMode_SIGN_MODE_DIRECT, - Signature: signatureBytes, - }, - Sequence: 0, - } - - txBuilder.SetSignatures(sigsV2) - return TestCaseStruct{ - txBuilder: txBuilder, - expectedFeePayer: feePayer.String(), - expectedGas: 0, - expectedFee: math.NewInt(0), - expectedMemo: "", - expectedMsg: "", - expectedSignatureBytes: signatureBytes, - } -} - -func createPopulatedTestCase(t *testing.T) TestCaseStruct { - t.Helper() - basicTestCase := createBasicTestCase(t) - txBuilder := basicTestCase.txBuilder - - gasLimit := uint64(200000) - memo := "" - denom := evmtypes.DefaultParams().EvmDenom - feeAmount := math.NewInt(2000) - - txBuilder.SetFeeAmount(sdk.NewCoins( - sdk.NewCoin( - denom, - feeAmount, - ))) - - txBuilder.SetGasLimit(gasLimit) - txBuilder.SetMemo(memo) - - msgSend := banktypes.MsgSend{ - FromAddress: feePayerAddress, - ToAddress: "ethm12luku6uxehhak02py4rcz65zu0swh7wjun6msa", - Amount: sdk.NewCoins( - sdk.NewCoin( - evmtypes.DefaultParams().EvmDenom, - math.NewInt(10000000), - ), - ), - } - - txBuilder.SetMsgs(&msgSend) - - return TestCaseStruct{ - txBuilder: txBuilder, - expectedFeePayer: basicTestCase.expectedFeePayer, - expectedGas: gasLimit, - expectedFee: feeAmount, - expectedMemo: memo, - expectedMsg: msgSend.String(), - expectedSignatureBytes: basicTestCase.expectedSignatureBytes, - } -}