Add basic sign mode infrastructure (#6371)
* Add SignModeHandler and LegacyAminoJSONHandler * Simplify, add tests * Add godocs * Add handler map * Rename HandlerMap * Don't use pointer * Lint * Lint * Update x/auth/signing/amino/amino.go Co-authored-by: Marko <marbar3778@yahoo.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Marko <marbar3778@yahoo.com>
This commit is contained in:
parent
0786e68296
commit
08b7557fe5
|
@ -0,0 +1,47 @@
|
|||
package amino
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
types "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
// LegacyAminoJSONHandler is a SignModeHandler that handles SIGN_MODE_LEGACY_AMINO_JSON
|
||||
type LegacyAminoJSONHandler struct{}
|
||||
|
||||
var _ signing.SignModeHandler = LegacyAminoJSONHandler{}
|
||||
|
||||
// DefaultMode implements SignModeHandler.DefaultMode
|
||||
func (h LegacyAminoJSONHandler) DefaultMode() types.SignMode {
|
||||
return types.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
|
||||
}
|
||||
|
||||
// Modes implements SignModeHandler.Modes
|
||||
func (LegacyAminoJSONHandler) Modes() []types.SignMode {
|
||||
return []types.SignMode{types.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}
|
||||
}
|
||||
|
||||
// DefaultMode implements SignModeHandler.GetSignBytes
|
||||
func (LegacyAminoJSONHandler) GetSignBytes(mode types.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) {
|
||||
if mode != types.SignMode_SIGN_MODE_LEGACY_AMINO_JSON {
|
||||
return nil, fmt.Errorf("expected %s, got %s", types.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, mode)
|
||||
}
|
||||
|
||||
feeTx, ok := tx.(ante.FeeTx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected FeeTx, got %T", tx)
|
||||
}
|
||||
|
||||
memoTx, ok := tx.(ante.TxWithMemo)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected TxWithMemo, got %T", tx)
|
||||
}
|
||||
|
||||
return authtypes.StdSignBytes(
|
||||
data.ChainID, data.AccountNumber, data.AccountSequence, authtypes.StdFee{Amount: feeTx.GetFee(), Gas: feeTx.GetGas()}, tx.GetMsgs(), memoTx.GetMemo(), // nolint:staticcheck // SA1019: authtypes.StdFee is deprecated, will be removed once proto migration is completed
|
||||
), nil
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package amino_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing/amino"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
func TestLegacyAminoJSONHandler_GetSignBytes(t *testing.T) {
|
||||
priv1 := secp256k1.GenPrivKey()
|
||||
addr1 := sdk.AccAddress(priv1.PubKey().Address())
|
||||
priv2 := secp256k1.GenPrivKey()
|
||||
addr2 := sdk.AccAddress(priv2.PubKey().Address())
|
||||
|
||||
coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}
|
||||
|
||||
fee := auth.StdFee{
|
||||
Amount: coins,
|
||||
Gas: 10000,
|
||||
}
|
||||
memo := "foo"
|
||||
msgs := []sdk.Msg{
|
||||
&bank.MsgSend{
|
||||
FromAddress: addr1,
|
||||
ToAddress: addr2,
|
||||
Amount: coins,
|
||||
},
|
||||
}
|
||||
|
||||
tx := auth.StdTx{
|
||||
Msgs: msgs,
|
||||
Fee: fee,
|
||||
Signatures: nil,
|
||||
Memo: memo,
|
||||
}
|
||||
|
||||
var (
|
||||
chainId = "test-chain"
|
||||
accNum uint64 = 7
|
||||
seqNum uint64 = 7
|
||||
)
|
||||
|
||||
handler := amino.LegacyAminoJSONHandler{}
|
||||
signingData := signing.SignerData{
|
||||
ChainID: chainId,
|
||||
AccountNumber: accNum,
|
||||
AccountSequence: seqNum,
|
||||
}
|
||||
signBz, err := handler.GetSignBytes(txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedSignBz := auth.StdSignBytes(chainId, accNum, seqNum, fee, msgs, memo)
|
||||
|
||||
require.Equal(t, expectedSignBz, signBz)
|
||||
|
||||
// expect error with wrong sign mode
|
||||
_, err = handler.GetSignBytes(txtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestLegacyAminoJSONHandler_DefaultMode(t *testing.T) {
|
||||
handler := amino.LegacyAminoJSONHandler{}
|
||||
require.Equal(t, txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode())
|
||||
}
|
||||
|
||||
func TestLegacyAminoJSONHandler_Modes(t *testing.T) {
|
||||
handler := amino.LegacyAminoJSONHandler{}
|
||||
require.Equal(t, []txtypes.SignMode{txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}, handler.Modes())
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package signing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
types "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
)
|
||||
|
||||
// SignModeHandlerMap is SignModeHandler that aggregates multiple SignModeHandler's into
|
||||
// a single handler
|
||||
type SignModeHandlerMap struct {
|
||||
defaultMode types.SignMode
|
||||
modes []types.SignMode
|
||||
signModeHandlers map[types.SignMode]SignModeHandler
|
||||
}
|
||||
|
||||
var _ SignModeHandler = SignModeHandlerMap{}
|
||||
|
||||
// NewSignModeHandlerMap returns a new SignModeHandlerMap with the provided defaultMode and handlers
|
||||
func NewSignModeHandlerMap(defaultMode types.SignMode, handlers []SignModeHandler) SignModeHandlerMap {
|
||||
handlerMap := make(map[types.SignMode]SignModeHandler)
|
||||
var modes []types.SignMode
|
||||
|
||||
for _, h := range handlers {
|
||||
for _, m := range h.Modes() {
|
||||
if _, have := handlerMap[m]; have {
|
||||
panic(fmt.Errorf("duplicate sign mode handler for mode %s", m))
|
||||
}
|
||||
handlerMap[m] = h
|
||||
modes = append(modes, m)
|
||||
}
|
||||
}
|
||||
|
||||
return SignModeHandlerMap{
|
||||
defaultMode: defaultMode,
|
||||
modes: modes,
|
||||
signModeHandlers: handlerMap,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultMode implements SignModeHandler.DefaultMode
|
||||
func (h SignModeHandlerMap) DefaultMode() types.SignMode {
|
||||
return h.defaultMode
|
||||
}
|
||||
|
||||
// Modes implements SignModeHandler.Modes
|
||||
func (h SignModeHandlerMap) Modes() []types.SignMode {
|
||||
return h.modes
|
||||
}
|
||||
|
||||
// DefaultMode implements SignModeHandler.GetSignBytes
|
||||
func (h SignModeHandlerMap) GetSignBytes(mode types.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)
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package signing_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing/amino"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
func MakeTestHandlerMap() signing.SignModeHandler {
|
||||
return signing.NewSignModeHandlerMap(
|
||||
txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
[]signing.SignModeHandler{
|
||||
amino.LegacyAminoJSONHandler{},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestHandlerMap_GetSignBytes(t *testing.T) {
|
||||
priv1 := secp256k1.GenPrivKey()
|
||||
addr1 := sdk.AccAddress(priv1.PubKey().Address())
|
||||
priv2 := secp256k1.GenPrivKey()
|
||||
addr2 := sdk.AccAddress(priv2.PubKey().Address())
|
||||
|
||||
coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}
|
||||
|
||||
fee := authtypes.StdFee{
|
||||
Amount: coins,
|
||||
Gas: 10000,
|
||||
}
|
||||
memo := "foo"
|
||||
msgs := []sdk.Msg{
|
||||
&bank.MsgSend{
|
||||
FromAddress: addr1,
|
||||
ToAddress: addr2,
|
||||
Amount: coins,
|
||||
},
|
||||
}
|
||||
|
||||
tx := authtypes.StdTx{
|
||||
Msgs: msgs,
|
||||
Fee: fee,
|
||||
Signatures: nil,
|
||||
Memo: memo,
|
||||
}
|
||||
|
||||
var (
|
||||
chainId = "test-chain"
|
||||
accNum uint64 = 7
|
||||
seqNum uint64 = 7
|
||||
)
|
||||
|
||||
handler := MakeTestHandlerMap()
|
||||
aminoJSONHandler := amino.LegacyAminoJSONHandler{}
|
||||
|
||||
signingData := signing.SignerData{
|
||||
ChainID: chainId,
|
||||
AccountNumber: accNum,
|
||||
AccountSequence: seqNum,
|
||||
}
|
||||
signBz, err := handler.GetSignBytes(txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedSignBz, err := aminoJSONHandler.GetSignBytes(txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expectedSignBz, signBz)
|
||||
|
||||
// expect error with wrong sign mode
|
||||
_, err = aminoJSONHandler.GetSignBytes(txtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestHandlerMap_DefaultMode(t *testing.T) {
|
||||
handler := MakeTestHandlerMap()
|
||||
require.Equal(t, txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode())
|
||||
}
|
||||
|
||||
func TestHandlerMap_Modes(t *testing.T) {
|
||||
handler := MakeTestHandlerMap()
|
||||
modes := handler.Modes()
|
||||
require.Contains(t, modes, txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
|
||||
require.Len(t, modes, 1)
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package signing
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
)
|
||||
|
||||
// SignModeHandler defines a interface to be implemented by types which will handle
|
||||
// SignMode's by generating sign bytes from a Tx and SignerData
|
||||
type SignModeHandler interface {
|
||||
// DefaultMode is the default mode that is to be used with this handler if no
|
||||
// other mode is specified. This can be useful for testing and CLI usage
|
||||
DefaultMode() txtypes.SignMode
|
||||
|
||||
// Modes is the list of modes supporting by this handler
|
||||
Modes() []txtypes.SignMode
|
||||
|
||||
// GetSignBytes returns the sign bytes for the provided SignMode, SignerData and Tx,
|
||||
// or an error
|
||||
GetSignBytes(mode txtypes.SignMode, data SignerData, tx sdk.Tx) ([]byte, error)
|
||||
}
|
||||
|
||||
// SignerData is the specific information needed to sign a transaction that generally
|
||||
// isn't included in the transaction body itself
|
||||
type SignerData struct {
|
||||
// ChainID is the chain that this transaction is targeted
|
||||
ChainID string
|
||||
|
||||
// AccountNumber is the account number of the signer
|
||||
AccountNumber uint64
|
||||
|
||||
// AccountSequence is the account sequence number of the signer that is used
|
||||
// for replay protection
|
||||
AccountSequence uint64
|
||||
}
|
Loading…
Reference in New Issue