ante: EIP712 support (#950)
* code migrated * signed_data ported to avoid conficting dependency * correct payload * eip712 working with evmos.me * use geth TypedData types * fix linter * minor refactor * test first try * fix test * fix tests * enforce fee delegated eip712 * verify signature refactor * SignedTypedData api refactor * add AnteHandler test for EIP712 * remove comment * code clean up * return more detailed error messages * fix linter * remove unnecesary global vars * Update app/ante/eip712.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * fix pr comments * remove hardcoded value * add more tests * add changelog * use sdk errors * add MsgDelegate test Co-authored-by: Freddy Caceres <freddy.caceres@crypto.com> Co-authored-by: Federico Kunze Küllmer <federico.kunze94@gmail.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: crypto-facs <84574577+crypto-facs@users.noreply.github.com>
This commit is contained in:
parent
0d69a69625
commit
3bea3fa1ef
@ -43,12 +43,16 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
* (log) [#948](https://github.com/tharsis/ethermint/pull/948) redirect go-ethereum's logs to Cosmos SDK logger.
|
||||
|
||||
### Features
|
||||
* (ante) [#950](https://github.com/tharsis/ethermint/pull/950) Add support for EIP712 signed Cosmos transactions
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* (rpc) [#955](https://github.com/tharsis/ethermint/pull/955) Fix websocket server push duplicated messages to subscriber.
|
||||
* (rpc) [tharsis#953](https://github.com/tharsis/ethermint/pull/953) Add `eth_signTypedData` api support.
|
||||
* (log) [#948](https://github.com/tharsis/ethermint/pull/948) redirect go-ethereum's logs to cosmos-sdk logger.
|
||||
|
||||
|
||||
## [v0.10.0-beta1] - 2022-02-15
|
||||
|
||||
### API Breaking
|
||||
|
@ -39,6 +39,9 @@ func NewAnteHandler(options HandlerOptions) sdk.AnteHandler {
|
||||
case "/ethermint.evm.v1.ExtensionOptionsEthereumTx":
|
||||
// handle as *evmtypes.MsgEthereumTx
|
||||
anteHandler = newEthAnteHandler(options)
|
||||
case "/ethermint.types.v1.ExtensionOptionsWeb3Tx":
|
||||
// handle as normal Cosmos SDK tx, except signature is checked for EIP712 representation
|
||||
anteHandler = newCosmosAnteHandlerEip712(options)
|
||||
default:
|
||||
return ctx, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrUnknownExtensionOptions,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ante_test
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
@ -292,6 +293,101 @@ func (suite AnteTestSuite) TestAnteHandler() {
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"success - DeliverTx EIP712 signed Cosmos Tx with MsgSend",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"success - DeliverTx EIP712 signed Cosmos Tx with DelegateMsg",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712TxBuilderMsgDelegate(from, privKey, "ethermint_9000-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, true,
|
||||
},
|
||||
{
|
||||
"fails - DeliverTx EIP712 signed Cosmos Tx with wrong Chain ID",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9002-1", gas, amount)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"fails - DeliverTx EIP712 signed Cosmos Tx with different gas fees",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9001-1", gas, amount)
|
||||
txBuilder.SetGasLimit(uint64(300000))
|
||||
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(30))))
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"fails - DeliverTx EIP712 signed Cosmos Tx with empty signature",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9001-1", gas, amount)
|
||||
sigsV2 := signing.SignatureV2{}
|
||||
txBuilder.SetSignatures(sigsV2)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"fails - DeliverTx EIP712 signed Cosmos Tx with invalid sequence",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9001-1", gas, amount)
|
||||
nonce, err := suite.app.AccountKeeper.GetSequence(suite.ctx, acc.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
sigsV2 := signing.SignatureV2{
|
||||
PubKey: privKey.PubKey(),
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
},
|
||||
Sequence: nonce - 1,
|
||||
}
|
||||
txBuilder.SetSignatures(sigsV2)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
{
|
||||
"fails - DeliverTx EIP712 signed Cosmos Tx with invalid signMode",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
|
||||
gas := uint64(200000)
|
||||
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9001-1", gas, amount)
|
||||
nonce, err := suite.app.AccountKeeper.GetSequence(suite.ctx, acc.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
sigsV2 := signing.SignatureV2{
|
||||
PubKey: privKey.PubKey(),
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signing.SignMode_SIGN_MODE_UNSPECIFIED,
|
||||
},
|
||||
Sequence: nonce,
|
||||
}
|
||||
txBuilder.SetSignatures(sigsV2)
|
||||
return txBuilder.GetTx()
|
||||
}, false, false, false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
272
app/ante/eip712.go
Normal file
272
app/ante/eip712.go
Normal file
@ -0,0 +1,272 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
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"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
||||
"github.com/tharsis/ethermint/ethereum/eip712"
|
||||
ethermint "github.com/tharsis/ethermint/types"
|
||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
var ethermintCodec codec.ProtoCodecMarshaler
|
||||
|
||||
func init() {
|
||||
registry := codectypes.NewInterfaceRegistry()
|
||||
ethermint.RegisterInterfaces(registry)
|
||||
ethermintCodec = codec.NewProtoCodec(registry)
|
||||
}
|
||||
|
||||
// Eip712SigVerificationDecorator Verify all signatures for a tx and return an error if any are invalid. Note,
|
||||
// the Eip712SigVerificationDecorator decorator will not get executed on ReCheck.
|
||||
//
|
||||
// CONTRACT: Pubkeys are set in context for all signers before this decorator runs
|
||||
// CONTRACT: Tx must implement SigVerifiableTx interface
|
||||
type Eip712SigVerificationDecorator struct {
|
||||
ak evmtypes.AccountKeeper
|
||||
signModeHandler authsigning.SignModeHandler
|
||||
}
|
||||
|
||||
// NewEip712SigVerificationDecorator creates a new Eip712SigVerificationDecorator
|
||||
func NewEip712SigVerificationDecorator(ak evmtypes.AccountKeeper, signModeHandler authsigning.SignModeHandler) Eip712SigVerificationDecorator {
|
||||
return Eip712SigVerificationDecorator{
|
||||
ak: ak,
|
||||
signModeHandler: signModeHandler,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle handles validation of EIP712 signed cosmos txs.
|
||||
// it is not run on RecheckTx
|
||||
func (svd Eip712SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
// no need to verify signatures on recheck tx
|
||||
if ctx.IsReCheckTx() {
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
sigTx, ok := tx.(authsigning.SigVerifiableTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx %T doesn't implement authsigning.SigVerifiableTx", tx)
|
||||
}
|
||||
|
||||
authSignTx, ok := tx.(authsigning.Tx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx %T doesn't implement the authsigning.Tx interface", tx)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
signerAddrs := sigTx.GetSigners()
|
||||
|
||||
// EIP712 allows just one signature
|
||||
if len(sigs) != 1 {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signers (%d); EIP712 signatures allows just one signature", len(sigs))
|
||||
}
|
||||
|
||||
// check that signer length and signature length are the same
|
||||
if len(sigs) != len(signerAddrs) {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signerAddrs), len(sigs))
|
||||
}
|
||||
|
||||
// EIP712 has just one signature, avoid looping here and only read index 0
|
||||
i := 0
|
||||
sig := sigs[i]
|
||||
|
||||
acc, err := authante.GetSignerAcc(ctx, svd.ak, signerAddrs[i])
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
// retrieve pubkey
|
||||
pubKey := acc.GetPubKey()
|
||||
if !simulate && pubKey == nil {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set")
|
||||
}
|
||||
|
||||
// Check account sequence number.
|
||||
if sig.Sequence != acc.GetSequence() {
|
||||
return ctx, sdkerrors.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()
|
||||
}
|
||||
|
||||
signerData := authsigning.SignerData{
|
||||
ChainID: chainID,
|
||||
AccountNumber: accNum,
|
||||
Sequence: acc.GetSequence(),
|
||||
}
|
||||
|
||||
if simulate {
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
if err := VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, authSignTx); err != nil {
|
||||
errMsg := fmt.Errorf("signature verification failed; please verify account number (%d) and chain-id (%s): %w", accNum, chainID, err)
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg.Error())
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// VerifySignature verifies a transaction signature contained in SignatureData abstracting over different signing modes
|
||||
// and single vs multi-signatures.
|
||||
func VerifySignature(
|
||||
pubKey cryptotypes.PubKey,
|
||||
signerData authsigning.SignerData,
|
||||
sigData signing.SignatureData,
|
||||
_ authsigning.SignModeHandler,
|
||||
tx authsigning.Tx,
|
||||
) error {
|
||||
switch data := sigData.(type) {
|
||||
case *signing.SingleSignatureData:
|
||||
if data.SignMode != signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrNotSupported, "unexpected SignatureData %T: wrong SignMode", sigData)
|
||||
}
|
||||
|
||||
// Note: this prevents the user from sending thrash data in the signature field
|
||||
if len(data.Signature) != 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrTooManySignatures, "invalid signature value; EIP712 must have the cosmos transaction signature empty")
|
||||
}
|
||||
|
||||
// @contract: this code is reached only when Msg has Web3Tx extension (so this custom Ante handler flow),
|
||||
// and the signature is SIGN_MODE_LEGACY_AMINO_JSON which is supported for EIP712 for now
|
||||
|
||||
msgs := tx.GetMsgs()
|
||||
if len(msgs) == 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrNoSignatures, "tx doesn't contain any msgs to verify signature")
|
||||
}
|
||||
|
||||
txBytes := legacytx.StdSignBytes(
|
||||
signerData.ChainID,
|
||||
signerData.AccountNumber,
|
||||
signerData.Sequence,
|
||||
tx.GetTimeoutHeight(),
|
||||
legacytx.StdFee{
|
||||
Amount: tx.GetFee(),
|
||||
Gas: tx.GetGas(),
|
||||
},
|
||||
msgs, tx.GetMemo(),
|
||||
)
|
||||
|
||||
signerChainID, err := ethermint.ParseChainID(signerData.ChainID)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrapf(err, "failed to parse chainID: %s", signerData.ChainID)
|
||||
}
|
||||
|
||||
txWithExtensions, ok := tx.(authante.HasExtensionOptionsTx)
|
||||
if !ok {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, "tx doesnt contain any extensions")
|
||||
}
|
||||
opts := txWithExtensions.GetExtensionOptions()
|
||||
if len(opts) != 1 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, "tx doesnt contain expected amount of extension options")
|
||||
}
|
||||
|
||||
var optIface ethermint.ExtensionOptionsWeb3TxI
|
||||
|
||||
if err := ethermintCodec.UnpackAny(opts[0], &optIface); err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to proto-unpack ExtensionOptionsWeb3Tx")
|
||||
}
|
||||
|
||||
extOpt, ok := optIface.(*ethermint.ExtensionOptionsWeb3Tx)
|
||||
if !ok {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidChainID, "unknown extension option")
|
||||
}
|
||||
|
||||
if extOpt.TypedDataChainID != signerChainID.Uint64() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidChainID, "invalid chainID")
|
||||
}
|
||||
|
||||
if len(extOpt.FeePayer) == 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, "no feePayer on ExtensionOptionsWeb3Tx")
|
||||
}
|
||||
feePayer, err := sdk.AccAddressFromBech32(extOpt.FeePayer)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to parse feePayer from ExtensionOptionsWeb3Tx")
|
||||
}
|
||||
|
||||
feeDelegation := &eip712.FeeDelegationOptions{
|
||||
FeePayer: feePayer,
|
||||
}
|
||||
|
||||
typedData, err := eip712.WrapTxToTypedData(ethermintCodec, extOpt.TypedDataChainID, msgs[0], txBytes, feeDelegation)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to pack tx data in EIP712 object")
|
||||
}
|
||||
|
||||
sigHash, err := eip712.ComputeTypedDataHash(typedData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
feePayerSig := extOpt.FeePayerSig
|
||||
if len(feePayerSig) != ethcrypto.SignatureLength {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, "signature length doesn't match typical [R||S||V] signature 65 bytes")
|
||||
}
|
||||
|
||||
// Remove the recovery offset if needed (ie. Metamask eip712 signature)
|
||||
if feePayerSig[ethcrypto.RecoveryIDOffset] == 27 || feePayerSig[ethcrypto.RecoveryIDOffset] == 28 {
|
||||
feePayerSig[ethcrypto.RecoveryIDOffset] -= 27
|
||||
}
|
||||
|
||||
feePayerPubkey, err := secp256k1.RecoverPubkey(sigHash, feePayerSig)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to recover delegated fee payer from sig")
|
||||
}
|
||||
|
||||
ecPubKey, err := ethcrypto.UnmarshalPubkey(feePayerPubkey)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to unmarshal recovered fee payer pubkey")
|
||||
}
|
||||
|
||||
pk := ðsecp256k1.PubKey{
|
||||
Key: ethcrypto.CompressPubkey(ecPubKey),
|
||||
}
|
||||
|
||||
if !pubKey.Equals(pk) {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "feePayer pubkey %s is different from transaction pubkey %s", pubKey, pk)
|
||||
}
|
||||
|
||||
recoveredFeePayerAcc := sdk.AccAddress(pk.Address().Bytes())
|
||||
|
||||
if !recoveredFeePayerAcc.Equals(feePayer) {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrorInvalidSigner, "failed to verify delegated fee payer %s signature", recoveredFeePayerAcc)
|
||||
}
|
||||
|
||||
// VerifySignature of ethsecp256k1 accepts 64 byte signature [R||S]
|
||||
// WARNING! Under NO CIRCUMSTANCES try to use pubKey.VerifySignature there
|
||||
if !secp256k1.VerifySignature(pubKey.Bytes(), sigHash, feePayerSig[:len(feePayerSig)-1]) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, "unable to verify signer signature of EIP712 typed data")
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrTooManySignatures, "unexpected SignatureData %T", sigData)
|
||||
}
|
||||
}
|
@ -79,3 +79,26 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
|
||||
ibcante.NewAnteDecorator(options.IBCChannelKeeper),
|
||||
)
|
||||
}
|
||||
|
||||
func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
|
||||
return sdk.ChainAnteDecorators(
|
||||
RejectMessagesDecorator{}, // reject MsgEthereumTxs
|
||||
ante.NewSetUpContextDecorator(),
|
||||
// NOTE: extensions option decorator removed
|
||||
// ante.NewRejectExtensionOptionsDecorator(),
|
||||
ante.NewMempoolFeeDecorator(),
|
||||
ante.NewValidateBasicDecorator(),
|
||||
ante.NewTxTimeoutHeightDecorator(),
|
||||
ante.NewValidateMemoDecorator(options.AccountKeeper),
|
||||
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
|
||||
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper),
|
||||
// SetPubKeyDecorator must be called before all signature verification decorators
|
||||
ante.NewSetPubKeyDecorator(options.AccountKeeper),
|
||||
ante.NewValidateSigCountDecorator(options.AccountKeeper),
|
||||
ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
|
||||
// Note: signature verification uses EIP instead of the cosmos signature validator
|
||||
NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
|
||||
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
|
||||
ibcante.NewAnteDecorator(options.IBCChannelKeeper),
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,13 @@
|
||||
package ante_test
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
|
||||
types2 "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
types3 "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/tharsis/ethermint/ethereum/eip712"
|
||||
"github.com/tharsis/ethermint/types"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
@ -191,6 +198,88 @@ func (suite *AnteTestSuite) CreateTestTxBuilder(
|
||||
return txBuilder
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSend(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
// Build MsgSend
|
||||
recipient := sdk.AccAddress(common.Address{}.Bytes())
|
||||
msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(1))))
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
|
||||
// Build MsgSend
|
||||
valEthAddr := tests.GenerateAddress()
|
||||
valAddr := sdk.ValAddress(valEthAddr.Bytes())
|
||||
msgSend := types3.NewMsgDelegate(from, valAddr, sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
|
||||
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
|
||||
from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msg sdk.Msg,
|
||||
) client.TxBuilder {
|
||||
var err error
|
||||
|
||||
nonce, err := suite.app.AccountKeeper.GetSequence(suite.ctx, from)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
pc, err := types.ParseChainID(chainId)
|
||||
suite.Require().NoError(err)
|
||||
ethChainId := pc.Uint64()
|
||||
|
||||
// GenerateTypedData TypedData
|
||||
var ethermintCodec codec.ProtoCodecMarshaler
|
||||
fee := legacytx.NewStdFee(gas, gasAmount)
|
||||
accNumber := suite.app.AccountKeeper.GetAccount(suite.ctx, from).GetAccountNumber()
|
||||
|
||||
data := legacytx.StdSignBytes(chainId, accNumber, nonce, 0, fee, []sdk.Msg{msg}, "")
|
||||
typedData, err := eip712.WrapTxToTypedData(ethermintCodec, ethChainId, msg, data, &eip712.FeeDelegationOptions{
|
||||
FeePayer: from,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sigHash, err := eip712.ComputeTypedDataHash(typedData)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Sign typedData
|
||||
keyringSigner := tests.NewSigner(priv)
|
||||
signature, pubKey, err := keyringSigner.SignByAddress(from, sigHash)
|
||||
suite.Require().NoError(err)
|
||||
signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||
|
||||
// Add ExtensionOptionsWeb3Tx extension
|
||||
var option *codectypes.Any
|
||||
option, err = codectypes.NewAnyWithValue(&types.ExtensionOptionsWeb3Tx{
|
||||
FeePayer: from.String(),
|
||||
TypedDataChainID: ethChainId,
|
||||
FeePayerSig: signature,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.clientCtx.TxConfig.SignModeHandler()
|
||||
txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
|
||||
builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder)
|
||||
suite.Require().True(ok)
|
||||
|
||||
builder.SetExtensionOptions(option)
|
||||
builder.SetFeeAmount(gasAmount)
|
||||
builder.SetGasLimit(gas)
|
||||
|
||||
sigsV2 := signing.SignatureV2{
|
||||
PubKey: pubKey,
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
},
|
||||
Sequence: nonce,
|
||||
}
|
||||
|
||||
err = builder.SetSignatures(sigsV2)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = builder.SetMsgs(msg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
var _ sdk.Tx = &invalidTx{}
|
||||
|
||||
type invalidTx struct{}
|
||||
|
449
ethereum/eip712/eip712.go
Normal file
449
ethereum/eip712/eip712.go
Normal file
@ -0,0 +1,449 @@
|
||||
package eip712
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
)
|
||||
|
||||
// ComputeTypedDataHash computes keccak hash of typed data for signing.
|
||||
func ComputeTypedDataHash(typedData apitypes.TypedData) ([]byte, error) {
|
||||
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
|
||||
if err != nil {
|
||||
err = sdkerrors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
|
||||
if err != nil {
|
||||
err = sdkerrors.Wrap(err, "failed to pack and hash typedData primary type")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
|
||||
return crypto.Keccak256(rawData), nil
|
||||
}
|
||||
|
||||
// WrapTxToTypedData is an ultimate method that wraps Amino-encoded Cosmos Tx JSON data
|
||||
// into an EIP712-compatible TypedData request.
|
||||
func WrapTxToTypedData(
|
||||
cdc codectypes.AnyUnpacker,
|
||||
chainID uint64,
|
||||
msg sdk.Msg,
|
||||
data []byte,
|
||||
feeDelegation *FeeDelegationOptions,
|
||||
) (apitypes.TypedData, error) {
|
||||
txData := make(map[string]interface{})
|
||||
|
||||
if err := json.Unmarshal(data, &txData); err != nil {
|
||||
return apitypes.TypedData{}, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, "failed to JSON unmarshal data")
|
||||
}
|
||||
|
||||
domain := apitypes.TypedDataDomain{
|
||||
Name: "Cosmos Web3",
|
||||
Version: "1.0.0",
|
||||
ChainId: math.NewHexOrDecimal256(int64(chainID)),
|
||||
VerifyingContract: "cosmos",
|
||||
Salt: "0",
|
||||
}
|
||||
|
||||
msgTypes, err := extractMsgTypes(cdc, "MsgValue", msg)
|
||||
if err != nil {
|
||||
return apitypes.TypedData{}, err
|
||||
}
|
||||
|
||||
if feeDelegation != nil {
|
||||
feeInfo, ok := txData["fee"].(map[string]interface{})
|
||||
if !ok {
|
||||
return apitypes.TypedData{}, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "cannot parse fee from tx data")
|
||||
}
|
||||
|
||||
feeInfo["feePayer"] = feeDelegation.FeePayer.String()
|
||||
|
||||
// also patching msgTypes to include feePayer
|
||||
msgTypes["Fee"] = []apitypes.Type{
|
||||
{Name: "feePayer", Type: "string"},
|
||||
{Name: "amount", Type: "Coin[]"},
|
||||
{Name: "gas", Type: "string"},
|
||||
}
|
||||
}
|
||||
|
||||
typedData := apitypes.TypedData{
|
||||
Types: msgTypes,
|
||||
PrimaryType: "Tx",
|
||||
Domain: domain,
|
||||
Message: txData,
|
||||
}
|
||||
|
||||
return typedData, nil
|
||||
}
|
||||
|
||||
type FeeDelegationOptions struct {
|
||||
FeePayer sdk.AccAddress
|
||||
}
|
||||
|
||||
func extractMsgTypes(cdc codectypes.AnyUnpacker, msgTypeName string, msg sdk.Msg) (apitypes.Types, error) {
|
||||
rootTypes := apitypes.Types{
|
||||
"EIP712Domain": {
|
||||
{
|
||||
Name: "name",
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Name: "version",
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Name: "chainId",
|
||||
Type: "uint256",
|
||||
},
|
||||
{
|
||||
Name: "verifyingContract",
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Name: "salt",
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
"Tx": {
|
||||
{Name: "account_number", Type: "string"},
|
||||
{Name: "chain_id", Type: "string"},
|
||||
{Name: "fee", Type: "Fee"},
|
||||
{Name: "memo", Type: "string"},
|
||||
{Name: "msgs", Type: "Msg[]"},
|
||||
{Name: "sequence", Type: "string"},
|
||||
// Note timeout_height was removed because it was not getting filled with the legacyTx
|
||||
// {Name: "timeout_height", Type: "string"},
|
||||
},
|
||||
"Fee": {
|
||||
{Name: "amount", Type: "Coin[]"},
|
||||
{Name: "gas", Type: "string"},
|
||||
},
|
||||
"Coin": {
|
||||
{Name: "denom", Type: "string"},
|
||||
{Name: "amount", Type: "string"},
|
||||
},
|
||||
"Msg": {
|
||||
{Name: "type", Type: "string"},
|
||||
{Name: "value", Type: msgTypeName},
|
||||
},
|
||||
msgTypeName: {},
|
||||
}
|
||||
|
||||
if err := walkFields(cdc, rootTypes, msgTypeName, msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rootTypes, nil
|
||||
}
|
||||
|
||||
const typeDefPrefix = "_"
|
||||
|
||||
func walkFields(cdc codectypes.AnyUnpacker, typeMap apitypes.Types, rootType string, in interface{}) (err error) {
|
||||
defer doRecover(&err)
|
||||
|
||||
t := reflect.TypeOf(in)
|
||||
v := reflect.ValueOf(in)
|
||||
|
||||
for {
|
||||
if t.Kind() == reflect.Ptr ||
|
||||
t.Kind() == reflect.Interface {
|
||||
t = t.Elem()
|
||||
v = v.Elem()
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return traverseFields(cdc, typeMap, rootType, typeDefPrefix, t, v)
|
||||
}
|
||||
|
||||
type cosmosAnyWrapper struct {
|
||||
Type string `json:"type"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func traverseFields(
|
||||
cdc codectypes.AnyUnpacker,
|
||||
typeMap apitypes.Types,
|
||||
rootType string,
|
||||
prefix string,
|
||||
t reflect.Type,
|
||||
v reflect.Value,
|
||||
) error {
|
||||
n := t.NumField()
|
||||
|
||||
if prefix == typeDefPrefix {
|
||||
if len(typeMap[rootType]) == n {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
typeDef := sanitizeTypedef(prefix)
|
||||
if len(typeMap[typeDef]) == n {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
var field reflect.Value
|
||||
if v.IsValid() {
|
||||
field = v.Field(i)
|
||||
}
|
||||
|
||||
fieldType := t.Field(i).Type
|
||||
fieldName := jsonNameFromTag(t.Field(i).Tag)
|
||||
|
||||
if fieldType == cosmosAnyType {
|
||||
any, ok := field.Interface().(*codectypes.Any)
|
||||
if !ok {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrPackAny, "%T", field.Interface())
|
||||
}
|
||||
|
||||
anyWrapper := &cosmosAnyWrapper{
|
||||
Type: any.TypeUrl,
|
||||
}
|
||||
|
||||
if err := cdc.UnpackAny(any, &anyWrapper.Value); err != nil {
|
||||
return sdkerrors.Wrap(err, "failed to unpack Any in msg struct")
|
||||
}
|
||||
|
||||
fieldType = reflect.TypeOf(anyWrapper)
|
||||
field = reflect.ValueOf(anyWrapper)
|
||||
|
||||
// then continue as normal
|
||||
}
|
||||
|
||||
for {
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
|
||||
if field.IsValid() {
|
||||
field = field.Elem()
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if fieldType.Kind() == reflect.Interface {
|
||||
fieldType = reflect.TypeOf(field.Interface())
|
||||
continue
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Ptr {
|
||||
field = field.Elem()
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
var isCollection bool
|
||||
if fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice {
|
||||
if field.Len() == 0 {
|
||||
// skip empty collections from type mapping
|
||||
continue
|
||||
}
|
||||
|
||||
fieldType = fieldType.Elem()
|
||||
field = field.Index(0)
|
||||
isCollection = true
|
||||
}
|
||||
|
||||
for {
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
|
||||
if field.IsValid() {
|
||||
field = field.Elem()
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if fieldType.Kind() == reflect.Interface {
|
||||
fieldType = reflect.TypeOf(field.Interface())
|
||||
continue
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Ptr {
|
||||
field = field.Elem()
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
fieldPrefix := fmt.Sprintf("%s.%s", prefix, fieldName)
|
||||
|
||||
ethTyp := typToEth(fieldType)
|
||||
if len(ethTyp) > 0 {
|
||||
if prefix == typeDefPrefix {
|
||||
typeMap[rootType] = append(typeMap[rootType], apitypes.Type{
|
||||
Name: fieldName,
|
||||
Type: ethTyp,
|
||||
})
|
||||
} else {
|
||||
typeDef := sanitizeTypedef(prefix)
|
||||
typeMap[typeDef] = append(typeMap[typeDef], apitypes.Type{
|
||||
Name: fieldName,
|
||||
Type: ethTyp,
|
||||
})
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if fieldType.Kind() == reflect.Struct {
|
||||
|
||||
var fieldTypedef string
|
||||
|
||||
if isCollection {
|
||||
fieldTypedef = sanitizeTypedef(fieldPrefix) + "[]"
|
||||
} else {
|
||||
fieldTypedef = sanitizeTypedef(fieldPrefix)
|
||||
}
|
||||
|
||||
if prefix == typeDefPrefix {
|
||||
typeMap[rootType] = append(typeMap[rootType], apitypes.Type{
|
||||
Name: fieldName,
|
||||
Type: fieldTypedef,
|
||||
})
|
||||
} else {
|
||||
typeDef := sanitizeTypedef(prefix)
|
||||
typeMap[typeDef] = append(typeMap[typeDef], apitypes.Type{
|
||||
Name: fieldName,
|
||||
Type: fieldTypedef,
|
||||
})
|
||||
}
|
||||
|
||||
if err := traverseFields(cdc, typeMap, rootType, fieldPrefix, fieldType, field); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func jsonNameFromTag(tag reflect.StructTag) string {
|
||||
jsonTags := tag.Get("json")
|
||||
parts := strings.Split(jsonTags, ",")
|
||||
return parts[0]
|
||||
}
|
||||
|
||||
// _.foo_bar.baz -> TypeFooBarBaz
|
||||
//
|
||||
// this is needed for Geth's own signing code which doesn't
|
||||
// tolerate complex type names
|
||||
func sanitizeTypedef(str string) string {
|
||||
buf := new(bytes.Buffer)
|
||||
parts := strings.Split(str, ".")
|
||||
|
||||
for _, part := range parts {
|
||||
if part == "_" {
|
||||
buf.WriteString("Type")
|
||||
continue
|
||||
}
|
||||
|
||||
subparts := strings.Split(part, "_")
|
||||
for _, subpart := range subparts {
|
||||
buf.WriteString(strings.Title(subpart))
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
var (
|
||||
hashType = reflect.TypeOf(common.Hash{})
|
||||
addressType = reflect.TypeOf(common.Address{})
|
||||
bigIntType = reflect.TypeOf(big.Int{})
|
||||
cosmIntType = reflect.TypeOf(sdk.Int{})
|
||||
cosmosAnyType = reflect.TypeOf(&codectypes.Any{})
|
||||
)
|
||||
|
||||
// typToEth supports only basic types and arrays of basic types.
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
|
||||
func typToEth(typ reflect.Type) string {
|
||||
const str = "string"
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
return str
|
||||
case reflect.Bool:
|
||||
return "bool"
|
||||
case reflect.Int:
|
||||
return "int64"
|
||||
case reflect.Int8:
|
||||
return "int8"
|
||||
case reflect.Int16:
|
||||
return "int16"
|
||||
case reflect.Int32:
|
||||
return "int32"
|
||||
case reflect.Int64:
|
||||
return "int64"
|
||||
case reflect.Uint:
|
||||
return "uint64"
|
||||
case reflect.Uint8:
|
||||
return "uint8"
|
||||
case reflect.Uint16:
|
||||
return "uint16"
|
||||
case reflect.Uint32:
|
||||
return "uint32"
|
||||
case reflect.Uint64:
|
||||
return "uint64"
|
||||
case reflect.Slice:
|
||||
ethName := typToEth(typ.Elem())
|
||||
if len(ethName) > 0 {
|
||||
return ethName + "[]"
|
||||
}
|
||||
case reflect.Array:
|
||||
ethName := typToEth(typ.Elem())
|
||||
if len(ethName) > 0 {
|
||||
return ethName + "[]"
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if typ.Elem().ConvertibleTo(bigIntType) ||
|
||||
typ.Elem().ConvertibleTo(cosmIntType) {
|
||||
return str
|
||||
}
|
||||
case reflect.Struct:
|
||||
if typ.ConvertibleTo(hashType) ||
|
||||
typ.ConvertibleTo(addressType) ||
|
||||
typ.ConvertibleTo(bigIntType) ||
|
||||
typ.ConvertibleTo(cosmIntType) {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func doRecover(err *error) {
|
||||
if r := recover(); r != nil {
|
||||
if e, ok := r.(error); ok {
|
||||
e = sdkerrors.Wrap(e, "panicked with error")
|
||||
*err = e
|
||||
return
|
||||
}
|
||||
|
||||
*err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}
|
28
go.mod
28
go.mod
@ -17,15 +17,12 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/holiman/uint256 v1.2.0
|
||||
github.com/improbable-eng/grpc-web v0.15.0
|
||||
github.com/klauspost/compress v1.11.9 // indirect
|
||||
github.com/miguelmota/go-ethereum-hdwallet v0.1.1
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rakyll/statik v0.1.7
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/regen-network/cosmos-proto v0.3.1
|
||||
github.com/rs/cors v1.8.2
|
||||
github.com/rs/zerolog v1.26.0 // indirect
|
||||
github.com/spf13/cast v1.4.1
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/spf13/viper v1.10.1
|
||||
@ -34,8 +31,6 @@ require (
|
||||
github.com/tendermint/tendermint v0.34.14
|
||||
github.com/tendermint/tm-db v0.6.7
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220211171837-173942840c17
|
||||
google.golang.org/grpc v1.44.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
@ -46,8 +41,8 @@ require (
|
||||
filippo.io/edwards25519 v1.0.0-beta.2 // indirect
|
||||
github.com/99designs/keyring v1.1.6 // indirect
|
||||
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
|
||||
github.com/DataDog/zstd v1.4.8 // indirect
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/DataDog/zstd v1.4.5 // indirect
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
|
||||
github.com/Workiva/go-datastructures v1.0.52 // indirect
|
||||
github.com/armon/go-metrics v0.3.10 // indirect
|
||||
@ -77,15 +72,16 @@ require (
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
|
||||
github.com/go-kit/kit v0.10.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/go-ole/go-ole v1.2.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gogo/gateway v1.1.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/orderedcode v0.0.1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/google/uuid v1.1.5 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
@ -101,6 +97,7 @@ require (
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect
|
||||
github.com/klauspost/compress v1.11.7 // indirect
|
||||
github.com/lib/pq v1.10.2 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
@ -113,7 +110,6 @@ require (
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/onsi/gomega v1.17.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
@ -122,7 +118,9 @@ require (
|
||||
github.com/prometheus/common v0.29.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/prometheus/tsdb v0.7.1 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/rs/zerolog v1.23.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
@ -133,15 +131,17 @@ require (
|
||||
github.com/tendermint/btcd v0.1.1 // indirect
|
||||
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.7 // indirect
|
||||
github.com/tklauser/numcpus v0.2.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.5 // indirect
|
||||
github.com/tklauser/numcpus v0.2.2 // indirect
|
||||
github.com/zondax/hid v0.9.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506 // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.7 // indirect
|
||||
golang.org/x/tools v0.1.5 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 // indirect
|
||||
|
40
go.sum
40
go.sum
@ -75,9 +75,8 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/DataDog/zstd v1.4.8 h1:Rpmta4xZ/MgZnriKNd24iZMhGpP5dvUcs/uqfBapKZY=
|
||||
github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
|
||||
@ -88,9 +87,8 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o=
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
|
||||
@ -361,9 +359,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
@ -371,7 +368,6 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
@ -440,9 +436,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@ -485,9 +480,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
|
||||
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
@ -654,9 +648,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.9 h1:5OCMOdde1TCT2sookEuVeEZzA8bmRSFV3AwPDZAG8AA=
|
||||
github.com/klauspost/compress v1.11.9/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@ -801,9 +794,8 @@ github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
@ -909,9 +901,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
||||
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
|
||||
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg=
|
||||
github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM=
|
||||
github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
|
||||
@ -928,10 +919,8 @@ github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
|
||||
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g=
|
||||
github.com/rs/zerolog v1.23.0/go.mod h1:6c7hFfxPOy7TacJc4Fcdi24/J0NKYGzjG8FWRI916Qo=
|
||||
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
|
||||
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@ -1038,12 +1027,10 @@ github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
|
||||
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg=
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
|
||||
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||
github.com/tklauser/go-sysconf v0.3.7 h1:HT7h4+536gjqeq1ZIJPgOl1rg1XFatQGVZWp7Py53eg=
|
||||
github.com/tklauser/go-sysconf v0.3.7/go.mod h1:JZIdXh4RmBvZDBZ41ld2bGxRV3n4daiiqA3skYhAoQ4=
|
||||
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
||||
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
||||
github.com/tklauser/numcpus v0.2.3 h1:nQ0QYpiritP6ViFhrKYsiv6VVxOpum2Gks5GhnJbS/8=
|
||||
github.com/tklauser/numcpus v0.2.3/go.mod h1:vpEPS/JC+oZGGQ/My/vJnNsvMDQL6PwOqt8dsCw5j+E=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
@ -1077,7 +1064,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8=
|
||||
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
@ -1296,7 +1282,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1458,9 +1443,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/tharsis/ethermint/ethereum/eip712"
|
||||
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@ -446,18 +448,11 @@ func (e *PublicAPI) SignTypedData(address common.Address, typedData apitypes.Typ
|
||||
return nil, fmt.Errorf("%s; %s", keystore.ErrNoMatch, err.Error())
|
||||
}
|
||||
|
||||
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
|
||||
sigHash, err := eip712.ComputeTypedDataHash(typedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
|
||||
sigHash := crypto.Keccak256(rawData)
|
||||
// Sign the requested hash with the wallet
|
||||
signature, _, err := e.clientCtx.Keyring.SignByAddress(from, sigHash)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user