Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.
/ cosmos-sdk Public archive
forked from cosmos/cosmos-sdk

Commit

Permalink
feat(x/tx): add basic handler types + sign mode direct (cosmos#14787)
Browse files Browse the repository at this point in the history
Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>
  • Loading branch information
2 people authored and tsenart committed Apr 12, 2023
1 parent 2a7f4e4 commit 12abcc5
Show file tree
Hide file tree
Showing 23 changed files with 345 additions and 79 deletions.
31 changes: 31 additions & 0 deletions x/tx/signing/direct/direct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package direct

import (
"context"

signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
"google.golang.org/protobuf/proto"

"cosmossdk.io/x/tx/signing"
)

// SignModeHandler is the SIGN_MODE_DIRECT implementation of signing.SignModeHandler.
type SignModeHandler struct{}

// Mode implements signing.SignModeHandler.Mode.
func (h SignModeHandler) Mode() signingv1beta1.SignMode {
return signingv1beta1.SignMode_SIGN_MODE_DIRECT
}

// GetSignBytes implements signing.SignModeHandler.GetSignBytes.
func (SignModeHandler) GetSignBytes(_ context.Context, signerData signing.SignerData, txData signing.TxData) ([]byte, error) {
return proto.Marshal(&txv1beta1.SignDoc{
BodyBytes: txData.BodyBytes,
AuthInfoBytes: txData.AuthInfoBytes,
ChainId: signerData.ChainId,
AccountNumber: signerData.AccountNumber,
})
}

var _ signing.SignModeHandler = SignModeHandler{}
99 changes: 99 additions & 0 deletions x/tx/signing/direct/direct_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package direct_test

import (
"context"
"testing"

bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
"cosmossdk.io/api/cosmos/crypto/secp256k1"
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
"github.com/cosmos/cosmos-proto/any"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"

"cosmossdk.io/x/tx/signing"
"cosmossdk.io/x/tx/signing/direct"
)

func TestDirectModeHandler(t *testing.T) {
memo := "sometestmemo"

msg, err := any.New(&bankv1beta1.MsgSend{})
require.NoError(t, err)

pk, err := any.New(&secp256k1.PubKey{
Key: make([]byte, 256),
})
require.NoError(t, err)

accSeq := uint64(2) // Arbitrary account sequence

signerInfo := []*txv1beta1.SignerInfo{
{
PublicKey: pk,
ModeInfo: &txv1beta1.ModeInfo{
Sum: &txv1beta1.ModeInfo_Single_{
Single: &txv1beta1.ModeInfo_Single{
Mode: signingv1beta1.SignMode_SIGN_MODE_DIRECT,
},
},
},
Sequence: accSeq,
},
}

fee := &txv1beta1.Fee{Amount: []*basev1beta1.Coin{{Denom: "uatom", Amount: "1000"}}, GasLimit: 20000}
txBody := &txv1beta1.TxBody{
Messages: []*anypb.Any{msg},
Memo: memo,
}

authInfo := &txv1beta1.AuthInfo{
Fee: fee,
SignerInfos: signerInfo,
}

directHandler := direct.SignModeHandler{}

chainId := "test-chain"
accNum := uint64(1)

signingData := signing.SignerData{
Address: "",
ChainId: chainId,
AccountNumber: accNum,
PubKey: pk,
}

bodyBz, err := proto.Marshal(txBody)
require.NoError(t, err)

authInfoBz, err := proto.Marshal(authInfo)
require.NoError(t, err)

txData := signing.TxData{
Body: txBody,
AuthInfo: authInfo,
BodyBytes: bodyBz,
AuthInfoBytes: authInfoBz,
BodyHasUnknownNonCriticals: false,
}

signBytes, err := directHandler.GetSignBytes(context.Background(), signingData, txData)
require.NoError(t, err)
require.NotNil(t, signBytes)

signBytes2, err := proto.Marshal(&txv1beta1.SignDoc{
BodyBytes: txData.BodyBytes,
AuthInfoBytes: txData.AuthInfoBytes,
ChainId: chainId,
AccountNumber: accNum,
})
require.NoError(t, err)
require.NotNil(t, signBytes2)

require.Equal(t, signBytes2, signBytes)
}
45 changes: 45 additions & 0 deletions x/tx/signing/handler_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package signing

import (
"context"
"fmt"

signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
)

// HandlerMap aggregates several sign mode handlers together for convenient generation of sign bytes
// based on sign mode.
type HandlerMap struct {
signModeHandlers map[signingv1beta1.SignMode]SignModeHandler
modes []signingv1beta1.SignMode
}

// NewHandlerMap constructs a new sign mode handler map.
func NewHandlerMap(handlers ...SignModeHandler) *HandlerMap {
res := &HandlerMap{
signModeHandlers: map[signingv1beta1.SignMode]SignModeHandler{},
}

for _, handler := range handlers {
mode := handler.Mode()
res.signModeHandlers[mode] = handler
res.modes = append(res.modes, mode)
}

return res
}

// SupportedModes lists the modes supported by this handler map.
func (h *HandlerMap) SupportedModes() []signingv1beta1.SignMode {
return h.modes
}

// GetSignBytes returns the sign bytes for the transaction for the requested mode.
func (h *HandlerMap) GetSignBytes(ctx context.Context, signMode signingv1beta1.SignMode, signerData SignerData, txData TxData) ([]byte, error) {
handler, ok := h.signModeHandlers[signMode]
if !ok {
return nil, fmt.Errorf("unsuppored sign mode %s", signMode)
}

return handler.GetSignBytes(ctx, signerData, txData)
}
38 changes: 10 additions & 28 deletions x/tx/signing/sign_mode_handler.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,16 @@
package signing

import "google.golang.org/protobuf/types/known/anypb"
import (
"context"

// SignerData is the specific information needed to sign a transaction that generally
// isn't included in the transaction body itself
type SignerData struct {
// The address of the signer.
//
// In case of multisigs, this should be the multisig's address.
Address string
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
)

// ChainId is the chain that this transaction is targeted
ChainId string
// SignModeHandler is the interface that handlers for each sign mode should implement to generate sign bytes.
type SignModeHandler interface {
// Mode is the sign mode supported by this handler
Mode() signingv1beta1.SignMode

// AccountNumber is the account number of the signer.
//
// In case of multisigs, this should be the multisig account number.
AccountNumber uint64

// Sequence is the account sequence number of the signer that is used
// for replay protection. This field is only useful for Legacy Amino signing,
// since in SIGN_MODE_DIRECT the account sequence is already in the signer
// info.
//
// In case of multisigs, this should be the multisig sequence.
Sequence uint64

// PubKey is the public key of the signer.
//
// In case of multisigs, this should be the pubkey of the member of the
// multisig that is signing the current sign doc.
PubKey *anypb.Any
// GetSignBytes returns the sign bytes for the provided SignerData and TxData, or an error.
GetSignBytes(ctx context.Context, signerData SignerData, txData TxData) ([]byte, error)
}
34 changes: 34 additions & 0 deletions x/tx/signing/signer_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package signing

import "google.golang.org/protobuf/types/known/anypb"

// SignerData is the specific information needed to sign a transaction that generally
// isn't included in the transaction body itself
type SignerData struct {
// The address of the signer.
//
// In case of multisigs, this should be the multisig's address.
Address string

// ChainId is the chain that this transaction is targeted
ChainId string

// AccountNumber is the account number of the signer.
//
// In case of multisigs, this should be the multisig account number.
AccountNumber uint64

// Sequence is the account sequence number of the signer that is used
// for replay protection. This field is only useful for Legacy Amino signing,
// since in SIGN_MODE_DIRECT the account sequence is already in the signer
// info.
//
// In case of multisigs, this should be the multisig sequence.
Sequence uint64

// PubKey is the public key of the signer.
//
// In case of multisigs, this should be the pubkey of the member of the
// multisig that is signing the current sign doc.
PubKey *anypb.Any
}
28 changes: 28 additions & 0 deletions x/tx/signing/std/handler_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package std

import (
"fmt"

"cosmossdk.io/x/tx/signing"
"cosmossdk.io/x/tx/signing/direct"
"cosmossdk.io/x/tx/textual"
)

// SignModeOptions are options for configuring the standard sign mode handler map.
type SignModeOptions struct {
// CoinMetadataQueryFn is the CoinMetadataQueryFn required for SIGN_MODE_TEXTUAL.
CoinMetadataQueryFn textual.CoinMetadataQueryFn
}

// HandlerMap returns a sign mode handler map that Cosmos SDK apps can use out
// of the box to support all "standard" sign modes.
func (s SignModeOptions) HandlerMap() (*signing.HandlerMap, error) {
if s.CoinMetadataQueryFn == nil {
return nil, fmt.Errorf("missing %T needed for SIGN_MODE_TEXTUAL", s.CoinMetadataQueryFn)
}

return signing.NewHandlerMap(
direct.SignModeHandler{},
textual.NewSignModeHandler(s.CoinMetadataQueryFn),
), nil
}
23 changes: 23 additions & 0 deletions x/tx/signing/tx_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package signing

import txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"

// TxData is the data about a transaction that is necessary to generate sign bytes.
type TxData struct {
// Body is the TxBody that will be part of the transaction.
Body *txv1beta1.TxBody

// AuthInfo is the AuthInfo that will be part of the transaction.
AuthInfo *txv1beta1.AuthInfo

// BodyBytes is the marshaled body bytes that will be part of TxRaw.
BodyBytes []byte

// AuthInfoBytes is the marshaled AuthInfo bytes that will be part of TxRaw.
AuthInfoBytes []byte

// BodyHasUnknownNonCriticals should be set to true if the transaction has been
// decoded and found to have unknown non-critical fields. This is only needed
// for amino JSON signing.
BodyHasUnknownNonCriticals bool
}
4 changes: 2 additions & 2 deletions x/tx/textual/any.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import (

// anyValueRenderer is a ValueRenderer for google.protobuf.Any messages.
type anyValueRenderer struct {
tr *Textual
tr *SignModeHandler
}

// NewAnyValueRenderer returns a ValueRenderer for google.protobuf.Any messages.
func NewAnyValueRenderer(t *Textual) ValueRenderer {
func NewAnyValueRenderer(t *SignModeHandler) ValueRenderer {
return anyValueRenderer{tr: t}
}

Expand Down
7 changes: 4 additions & 3 deletions x/tx/textual/any_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (
"os"
"testing"

"cosmossdk.io/x/tx/textual"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"

"cosmossdk.io/x/tx/textual"

"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/testing/protocmp"
Expand All @@ -30,15 +31,15 @@ func TestAny(t *testing.T) {
err = json.Unmarshal(raw, &testcases)
require.NoError(t, err)

tr := textual.NewTextual(EmptyCoinMetadataQuerier)
tr := textual.NewSignModeHandler(EmptyCoinMetadataQuerier)
for i, tc := range testcases {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
anyMsg := anypb.Any{}
err = protojson.Unmarshal(tc.Proto, &anyMsg)
require.NoError(t, err)

// Format into screens and check vs expected
rend := textual.NewAnyValueRenderer((&tr))
rend := textual.NewAnyValueRenderer((tr))
screens, err := rend.Format(context.Background(), protoreflect.ValueOfMessage(anyMsg.ProtoReflect()))
require.NoError(t, err)
require.Equal(t, tc.Screens, screens)
Expand Down
5 changes: 3 additions & 2 deletions x/tx/textual/bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import (
"os"
"testing"

"cosmossdk.io/x/tx/textual"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/reflect/protoreflect"

"cosmossdk.io/x/tx/textual"
)

func TestBytesJsonTestCases(t *testing.T) {
Expand All @@ -20,7 +21,7 @@ func TestBytesJsonTestCases(t *testing.T) {
err = json.Unmarshal(raw, &testcases)
require.NoError(t, err)

textual := textual.NewTextual(nil)
textual := textual.NewSignModeHandler(nil)

for _, tc := range testcases {
t.Run(tc.hex, func(t *testing.T) {
Expand Down
Loading

0 comments on commit 12abcc5

Please sign in to comment.