feat(x/tx): add basic handler types + sign mode direct (#14787)
Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>
This commit is contained in:
parent
4f13b5b319
commit
8bd9288051
31
x/tx/signing/direct/direct.go
Normal file
31
x/tx/signing/direct/direct.go
Normal file
@ -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
x/tx/signing/direct/direct_test.go
Normal file
99
x/tx/signing/direct/direct_test.go
Normal file
@ -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
x/tx/signing/handler_map.go
Normal file
45
x/tx/signing/handler_map.go
Normal file
@ -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)
|
||||
}
|
||||
@ -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
x/tx/signing/signer_data.go
Normal file
34
x/tx/signing/signer_data.go
Normal file
@ -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
x/tx/signing/std/handler_map.go
Normal file
28
x/tx/signing/std/handler_map.go
Normal file
@ -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
x/tx/signing/tx_data.go
Normal file
23
x/tx/signing/tx_data.go
Normal file
@ -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
|
||||
}
|
||||
@ -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}
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
@ -30,7 +31,7 @@ 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{}
|
||||
@ -38,7 +39,7 @@ func TestAny(t *testing.T) {
|
||||
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)
|
||||
|
||||
@ -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) {
|
||||
@ -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) {
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
|
||||
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
)
|
||||
|
||||
@ -47,7 +48,7 @@ func addMetadataToContext(ctx context.Context, metadata *bankv1beta1.Metadata) c
|
||||
|
||||
func TestMetadataQuerier(t *testing.T) {
|
||||
// Errors on nil metadata querier
|
||||
txt := textual.NewTextual(nil)
|
||||
txt := textual.NewSignModeHandler(nil)
|
||||
vr, err := txt.GetFieldValueRenderer(fieldDescriptorFromName("COIN"))
|
||||
require.NoError(t, err)
|
||||
_, err = vr.Format(context.Background(), protoreflect.ValueOf((&basev1beta1.Coin{}).ProtoReflect()))
|
||||
@ -55,7 +56,7 @@ func TestMetadataQuerier(t *testing.T) {
|
||||
|
||||
// Errors if metadata querier returns an error
|
||||
expErr := fmt.Errorf("mock error")
|
||||
txt = textual.NewTextual(func(_ context.Context, _ string) (*bankv1beta1.Metadata, error) {
|
||||
txt = textual.NewSignModeHandler(func(_ context.Context, _ string) (*bankv1beta1.Metadata, error) {
|
||||
return nil, expErr
|
||||
})
|
||||
vr, err = txt.GetFieldValueRenderer(fieldDescriptorFromName("COIN"))
|
||||
@ -73,7 +74,7 @@ func TestCoinJsonTestcases(t *testing.T) {
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
textual := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
textual := textual.NewSignModeHandler(mockCoinMetadataQuerier)
|
||||
vr, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("COIN"))
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@ -9,9 +9,10 @@ import (
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
)
|
||||
|
||||
func TestCoinsJsonTestcases(t *testing.T) {
|
||||
@ -21,7 +22,7 @@ func TestCoinsJsonTestcases(t *testing.T) {
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
txt := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
txt := textual.NewSignModeHandler(mockCoinMetadataQuerier)
|
||||
vr, err := txt.GetFieldValueRenderer(fieldDescriptorFromName("COINS"))
|
||||
vrr := vr.(textual.RepeatedValueRenderer)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -5,9 +5,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 TestDecJsonTestcases(t *testing.T) {
|
||||
@ -18,7 +19,7 @@ func TestDecJsonTestcases(t *testing.T) {
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
textual := textual.NewTextual(nil)
|
||||
textual := textual.NewSignModeHandler(nil)
|
||||
|
||||
for _, tc := range testcases {
|
||||
tc := tc
|
||||
|
||||
@ -16,6 +16,8 @@ import (
|
||||
_ "cosmossdk.io/api/cosmos/crypto/multisig"
|
||||
_ "cosmossdk.io/api/cosmos/crypto/secp256k1"
|
||||
_ "cosmossdk.io/api/cosmos/gov/v1"
|
||||
|
||||
"cosmossdk.io/x/tx/signing"
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
"cosmossdk.io/x/tx/textual/internal/textualpb"
|
||||
)
|
||||
@ -37,8 +39,8 @@ func TestE2EJsonTestcases(t *testing.T) {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
_, bodyBz, _, authInfoBz, signerData := createTextualData(t, tc.Proto, tc.SignerData)
|
||||
|
||||
tr := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
rend := textual.NewTxValueRenderer(&tr)
|
||||
tr := textual.NewSignModeHandler(mockCoinMetadataQuerier)
|
||||
rend := textual.NewTxValueRenderer(tr)
|
||||
ctx := addMetadataToContext(context.Background(), tc.Metadata)
|
||||
|
||||
data := &textualpb.TextualData{
|
||||
@ -64,7 +66,10 @@ func TestE2EJsonTestcases(t *testing.T) {
|
||||
require.Equal(t, tc.Screens, screens)
|
||||
|
||||
// Make sure CBOR match.
|
||||
signDoc, err := tr.GetSignBytes(ctx, bodyBz, authInfoBz, signerData)
|
||||
signDoc, err := tr.GetSignBytes(ctx, signerData, signing.TxData{
|
||||
BodyBytes: bodyBz,
|
||||
AuthInfoBytes: authInfoBz,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.Cbor, hex.EncodeToString(signDoc))
|
||||
|
||||
|
||||
@ -7,13 +7,14 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
"cosmossdk.io/x/tx/textual/internal/testpb"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
"cosmossdk.io/x/tx/textual/internal/testpb"
|
||||
)
|
||||
|
||||
type enumTest struct {
|
||||
@ -28,7 +29,7 @@ func TestEnumJsonTestcases(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.Text, func(t *testing.T) {
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
)
|
||||
|
||||
@ -22,7 +23,7 @@ func TestIntJsonTestcases(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[0], func(t *testing.T) {
|
||||
|
||||
@ -14,12 +14,12 @@ import (
|
||||
)
|
||||
|
||||
type messageValueRenderer struct {
|
||||
tr *Textual
|
||||
tr *SignModeHandler
|
||||
msgDesc protoreflect.MessageDescriptor
|
||||
fds []protoreflect.FieldDescriptor
|
||||
}
|
||||
|
||||
func NewMessageValueRenderer(t *Textual, msgDesc protoreflect.MessageDescriptor) ValueRenderer {
|
||||
func NewMessageValueRenderer(t *SignModeHandler, msgDesc protoreflect.MessageDescriptor) ValueRenderer {
|
||||
fields := msgDesc.Fields()
|
||||
fds := make([]protoreflect.FieldDescriptor, 0, fields.Len())
|
||||
for i := 0; i < fields.Len(); i++ {
|
||||
|
||||
@ -11,9 +11,11 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
"cosmossdk.io/x/tx/textual/internal/testpb"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
@ -34,10 +36,10 @@ func TestMessageJsonTestcases(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) {
|
||||
rend := textual.NewMessageValueRenderer(&tr, (&testpb.Foo{}).ProtoReflect().Descriptor())
|
||||
rend := textual.NewMessageValueRenderer(tr, (&testpb.Foo{}).ProtoReflect().Descriptor())
|
||||
|
||||
screens, err := rend.Format(context.Background(), protoreflect.ValueOf(tc.Proto.ProtoReflect()))
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -9,10 +9,11 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
"cosmossdk.io/x/tx/textual/internal/testpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
"cosmossdk.io/x/tx/textual/internal/testpb"
|
||||
)
|
||||
|
||||
type repeatedJsonTest struct {
|
||||
@ -28,13 +29,13 @@ func TestRepeatedJsonTestcases(t *testing.T) {
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
tr := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
tr := textual.NewSignModeHandler(mockCoinMetadataQuerier)
|
||||
for i, tc := range testcases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
// Create a context.Context containing all coins metadata, to simulate
|
||||
// that they are in state.
|
||||
ctx := context.Background()
|
||||
rend := textual.NewMessageValueRenderer(&tr, (&testpb.Qux{}).ProtoReflect().Descriptor())
|
||||
rend := textual.NewMessageValueRenderer(tr, (&testpb.Qux{}).ProtoReflect().Descriptor())
|
||||
require.NoError(t, err)
|
||||
|
||||
screens, err := rend.Format(ctx, protoreflect.ValueOf(tc.Proto.ProtoReflect()))
|
||||
|
||||
@ -16,6 +16,7 @@ import (
|
||||
msg "cosmossdk.io/api/cosmos/msg/v1"
|
||||
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
|
||||
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
|
||||
|
||||
"cosmossdk.io/x/tx/textual/internal/textualpb"
|
||||
)
|
||||
|
||||
@ -28,7 +29,7 @@ var (
|
||||
)
|
||||
|
||||
type txValueRenderer struct {
|
||||
tr *Textual
|
||||
tr *SignModeHandler
|
||||
}
|
||||
|
||||
// NewTxValueRenderer returns a ValueRenderer for the protobuf
|
||||
@ -36,7 +37,7 @@ type txValueRenderer struct {
|
||||
// The reason we create a renderer for TextualData (and not directly Tx)
|
||||
// is that TextualData is a single place that contains all data needed
|
||||
// to create the `[]Screen` SignDoc.
|
||||
func NewTxValueRenderer(tr *Textual) ValueRenderer {
|
||||
func NewTxValueRenderer(tr *SignModeHandler) ValueRenderer {
|
||||
return txValueRenderer{
|
||||
tr: tr,
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import (
|
||||
_ "cosmossdk.io/api/cosmos/crypto/secp256k1"
|
||||
_ "cosmossdk.io/api/cosmos/gov/v1"
|
||||
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
|
||||
|
||||
"cosmossdk.io/x/tx/signing"
|
||||
"cosmossdk.io/x/tx/textual"
|
||||
"cosmossdk.io/x/tx/textual/internal/textualpb"
|
||||
@ -55,8 +56,8 @@ func TestTxJsonTestcases(t *testing.T) {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
txBody, bodyBz, txAuthInfo, authInfoBz, signerData := createTextualData(t, tc.Proto, tc.SignerData)
|
||||
|
||||
tr := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
rend := textual.NewTxValueRenderer(&tr)
|
||||
tr := textual.NewSignModeHandler(mockCoinMetadataQuerier)
|
||||
rend := textual.NewTxValueRenderer(tr)
|
||||
ctx := addMetadataToContext(context.Background(), tc.Metadata)
|
||||
|
||||
data := &textualpb.TextualData{
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
@ -13,21 +14,22 @@ import (
|
||||
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
|
||||
cosmos_proto "github.com/cosmos/cosmos-proto"
|
||||
|
||||
"cosmossdk.io/x/tx/signing"
|
||||
"cosmossdk.io/x/tx/textual/internal/textualpb"
|
||||
cosmos_proto "github.com/cosmos/cosmos-proto"
|
||||
)
|
||||
|
||||
// CoinMetadataQueryFn defines a function that queries state for the coin denom
|
||||
// metadata. It is meant to be passed as an argument into `NewTextual`.
|
||||
// metadata. It is meant to be passed as an argument into `NewSignModeHandler`.
|
||||
type CoinMetadataQueryFn func(ctx context.Context, denom string) (*bankv1beta1.Metadata, error)
|
||||
|
||||
// ValueRendererCreator is a function returning a textual.
|
||||
type ValueRendererCreator func(protoreflect.FieldDescriptor) ValueRenderer
|
||||
|
||||
// Textual holds the configuration for dispatching
|
||||
// SignModeHandler holds the configuration for dispatching
|
||||
// to specific value renderers for SIGN_MODE_TEXTUAL.
|
||||
type Textual struct {
|
||||
type SignModeHandler struct {
|
||||
// coinMetadataQuerier defines a function to query the coin metadata from
|
||||
// state. It should use bank module's `DenomsMetadata` gRPC query to fetch
|
||||
// each denom's associated metadata, either using the bank keeper (for
|
||||
@ -44,16 +46,15 @@ type Textual struct {
|
||||
messages map[protoreflect.FullName]ValueRenderer
|
||||
}
|
||||
|
||||
// NewTextual returns a new Textual which provides
|
||||
// value renderers.
|
||||
func NewTextual(q CoinMetadataQueryFn) Textual {
|
||||
t := Textual{coinMetadataQuerier: q}
|
||||
// NewSignModeHandler returns a new SignModeHandler which generates sign bytes and provides value renderers.
|
||||
func NewSignModeHandler(q CoinMetadataQueryFn) *SignModeHandler {
|
||||
t := &SignModeHandler{coinMetadataQuerier: q}
|
||||
t.init()
|
||||
return t
|
||||
}
|
||||
|
||||
// GetFieldValueRenderer returns the value renderer for the given FieldDescriptor.
|
||||
func (r *Textual) GetFieldValueRenderer(fd protoreflect.FieldDescriptor) (ValueRenderer, error) {
|
||||
func (r *SignModeHandler) GetFieldValueRenderer(fd protoreflect.FieldDescriptor) (ValueRenderer, error) {
|
||||
switch {
|
||||
// Scalars, such as sdk.Int and sdk.Dec encoded as strings.
|
||||
case fd.Kind() == protoreflect.StringKind:
|
||||
@ -106,7 +107,7 @@ func (r *Textual) GetFieldValueRenderer(fd protoreflect.FieldDescriptor) (ValueR
|
||||
// GetMessageValueRenderer is a specialization of GetValueRenderer for messages.
|
||||
// It is useful when the message type is discovered outside the context of a field,
|
||||
// e.g. when handling a google.protobuf.Any.
|
||||
func (r *Textual) GetMessageValueRenderer(md protoreflect.MessageDescriptor) (ValueRenderer, error) {
|
||||
func (r *SignModeHandler) GetMessageValueRenderer(md protoreflect.MessageDescriptor) (ValueRenderer, error) {
|
||||
fullName := md.FullName()
|
||||
vr, found := r.messages[fullName]
|
||||
if found {
|
||||
@ -119,7 +120,7 @@ func (r *Textual) GetMessageValueRenderer(md protoreflect.MessageDescriptor) (Va
|
||||
// custom scalar and message renderers.
|
||||
//
|
||||
// It is an idempotent method.
|
||||
func (r *Textual) init() {
|
||||
func (r *SignModeHandler) init() {
|
||||
if r.scalars == nil {
|
||||
r.scalars = map[string]ValueRendererCreator{}
|
||||
r.scalars["cosmos.Int"] = func(fd protoreflect.FieldDescriptor) ValueRenderer { return NewIntValueRenderer(fd) }
|
||||
@ -136,22 +137,22 @@ func (r *Textual) init() {
|
||||
}
|
||||
|
||||
// DefineScalar adds a value renderer to the given Cosmos scalar.
|
||||
func (r *Textual) DefineScalar(scalar string, vr ValueRendererCreator) {
|
||||
func (r *SignModeHandler) DefineScalar(scalar string, vr ValueRendererCreator) {
|
||||
r.init()
|
||||
r.scalars[scalar] = vr
|
||||
}
|
||||
|
||||
// DefineMessageRenderer adds a new custom message renderer.
|
||||
func (r *Textual) DefineMessageRenderer(name protoreflect.FullName, vr ValueRenderer) {
|
||||
func (r *SignModeHandler) DefineMessageRenderer(name protoreflect.FullName, vr ValueRenderer) {
|
||||
r.init()
|
||||
r.messages[name] = vr
|
||||
}
|
||||
|
||||
// GetSignBytes returns the transaction sign bytes.
|
||||
func (r *Textual) GetSignBytes(ctx context.Context, bodyBz, authInfoBz []byte, signerData signing.SignerData) ([]byte, error) {
|
||||
func (r *SignModeHandler) GetSignBytes(ctx context.Context, signerData signing.SignerData, txData signing.TxData) ([]byte, error) {
|
||||
data := &textualpb.TextualData{
|
||||
BodyBytes: bodyBz,
|
||||
AuthInfoBytes: authInfoBz,
|
||||
BodyBytes: txData.BodyBytes,
|
||||
AuthInfoBytes: txData.AuthInfoBytes,
|
||||
SignerData: &textualpb.SignerData{
|
||||
Address: signerData.Address,
|
||||
ChainId: signerData.ChainId,
|
||||
@ -179,3 +180,9 @@ func (r *Textual) GetSignBytes(ctx context.Context, bodyBz, authInfoBz []byte, s
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (r *SignModeHandler) Mode() signingv1beta1.SignMode {
|
||||
return signingv1beta1.SignMode_SIGN_MODE_TEXTUAL
|
||||
}
|
||||
|
||||
var _ signing.SignModeHandler = &SignModeHandler{}
|
||||
|
||||
@ -34,7 +34,7 @@ func TestDispatcher(t *testing.T) {
|
||||
for _, tc := range testcases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
textual := textual.NewTextual(nil)
|
||||
textual := textual.NewSignModeHandler(nil)
|
||||
rend, err := textual.GetFieldValueRenderer(fieldDescriptorFromName(tc.name))
|
||||
|
||||
if tc.expErr {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user