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:
Aaron Craelius 2020-06-09 11:19:18 -04:00 committed by GitHub
parent 0786e68296
commit 08b7557fe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 311 additions and 0 deletions

View File

@ -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
}

View File

@ -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())
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}