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