Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean Any interface #8167

Merged
merged 14 commits into from
Dec 18, 2020
12 changes: 2 additions & 10 deletions client/tx/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -263,22 +262,15 @@ func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) {
},
Sequence: txf.Sequence(),
}

if err := txb.SetSignatures(sig); err != nil {
return nil, err
}

any, ok := txb.(codectypes.IntoAny)
if !ok {
return nil, fmt.Errorf("cannot simulate tx that cannot be wrapped into any")
}
cached := any.AsAny().GetCachedValue()
protoTx, ok := cached.(*tx.Tx)
protoProvider, ok := txb.(tx.ProtoTxProvider)
if !ok {
return nil, fmt.Errorf("cannot simulate amino tx")
}

simReq := tx.SimulateRequest{Tx: protoTx}
simReq := tx.SimulateRequest{Tx: protoProvider.GetProtoTx()}

return simReq.Marshal()
}
Expand Down
65 changes: 22 additions & 43 deletions codec/types/any.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,16 @@ type Any struct {
// returns an error if that value couldn't be packed. This also caches
// the packed value so that it can be retrieved from GetCachedValue without
// unmarshaling
func NewAnyWithValue(value proto.Message) (*Any, error) {
any := &Any{}

err := any.Pack(value)
if err != nil {
return nil, err
func NewAnyWithValue(v proto.Message) (*Any, error) {
if v == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, "Expecting non nil value to create a new Any")
}

return any, nil
bz, err := proto.Marshal(v)
return &Any{
TypeUrl: "/" + proto.MessageName(v),
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved
Value: bz,
cachedValue: v,
}, err
}

// NewAnyWithCustomTypeURL same as NewAnyWithValue, but sets a custom type url, instead
Expand All @@ -82,22 +83,6 @@ func NewAnyWithCustomTypeURL(v proto.Message, typeURL string) (*Any, error) {
}, err
}

// Pack packs the value x in the Any or returns an error. This also caches
// the packed value so that it can be retrieved from GetCachedValue without
// unmarshaling
func (any *Any) Pack(x proto.Message) error {
any.TypeUrl = "/" + proto.MessageName(x)
bz, err := proto.Marshal(x)
if err != nil {
return err
}

any.Value = bz
any.cachedValue = x

return nil
}

// UnsafePackAny packs the value x in the Any and instead of returning the error
// in the case of a packing failure, keeps the cached value. This should only
// be used in situations where compatibility is needed with amino. Amino-only
Expand All @@ -113,21 +98,20 @@ func UnsafePackAny(x interface{}) *Any {
return &Any{cachedValue: x}
}

// PackAny is a checked and safe version of UnsafePackAny. It assures that
// `x` implements the proto.Message interface and uses it to serialize `x`.
// [DEPRECATED]: should be moved away: https://github.com/cosmos/cosmos-sdk/issues/7479
func PackAny(x interface{}) (*Any, error) {
if x == nil {
return nil, nil
}
if intoany, ok := x.(IntoAny); ok {
return intoany.AsAny(), nil
}
protoMsg, ok := x.(proto.Message)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting %T to implement proto.Message", x)
// pack packs the value x in the Any or returns an error. This also caches
// the packed value so that it can be retrieved from GetCachedValue without
// unmarshaling
func (any *Any) pack(x proto.Message) error {
any.TypeUrl = "/" + proto.MessageName(x)
bz, err := proto.Marshal(x)
if err != nil {
return err
}
return NewAnyWithValue(protoMsg)

any.Value = bz
any.cachedValue = x

return nil
}

// GetCachedValue returns the cached value from the Any if present
Expand All @@ -139,8 +123,3 @@ func (any *Any) GetCachedValue() interface{} {
func (any *Any) ClearCachedValue() {
any.cachedValue = nil
}

// IntoAny represents a type that can be wrapped into an Any.
type IntoAny interface {
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
AsAny() *Any
}
6 changes: 2 additions & 4 deletions codec/types/compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error {
return err
}
if m, ok := val.(proto.Message); ok {
err := any.Pack(m)
if err != nil {
if err = any.pack(m); err != nil {
return err
}
} else {
Expand Down Expand Up @@ -148,8 +147,7 @@ func (a AminoJSONUnpacker) UnpackAny(any *Any, iface interface{}) error {
return err
}
if m, ok := val.(proto.Message); ok {
err := any.Pack(m)
if err != nil {
if err = any.pack(m); err != nil {
return err
}
} else {
Expand Down
21 changes: 8 additions & 13 deletions codec/types/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,19 @@ func TestPackUnpack(t *testing.T) {
registry := testdata.NewTestInterfaceRegistry()

spot := &testdata.Dog{Name: "Spot"}
any := types.Any{}
err := any.Pack(spot)
require.NoError(t, err)

require.Equal(t, spot, any.GetCachedValue())

// without cache
any.ClearCachedValue()
var animal testdata.Animal
err = registry.UnpackAny(&any, &animal)
require.NoError(t, err)
require.Equal(t, spot, animal)

// with cache
err = any.Pack(spot)
any, err := types.NewAnyWithValue(spot)
require.NoError(t, err)
require.Equal(t, spot, any.GetCachedValue())
err = registry.UnpackAny(any, &animal)
require.NoError(t, err)
err = registry.UnpackAny(&any, &animal)
require.Equal(t, spot, animal)

// without cache
any.ClearCachedValue()
err = registry.UnpackAny(any, &animal)
require.NoError(t, err)
require.Equal(t, spot, animal)
}
Expand Down
2 changes: 1 addition & 1 deletion simapp/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs
for _, val := range valSet.Validators {
pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey)
require.NoError(t, err)
pkAny, err := codectypes.PackAny(pk)
pkAny, err := codectypes.NewAnyWithValue(pk)
require.NoError(t, err)
validator := stakingtypes.Validator{
OperatorAddress: sdk.ValAddress(val.Address).String(),
Expand Down
5 changes: 5 additions & 0 deletions types/tx/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,8 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
func isServiceMsg(typeURL string) bool {
return strings.Count(typeURL, "/") >= 2
}

// ProtoTxProvider is a type which can provide a proto transaction.
type ProtoTxProvider interface {
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
GetProtoTx() *Tx
}
31 changes: 12 additions & 19 deletions x/auth/client/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func QueryTx(clientCtx client.Context, hashHexStr string) (*sdk.TxResponse, erro
return nil, err
}

out, err := formatTxResult(clientCtx.TxConfig, resTx, resBlocks[resTx.Height])
out, err := mkTxResult(clientCtx.TxConfig, resTx, resBlocks[resTx.Height])
if err != nil {
return out, err
}
Expand All @@ -102,7 +102,7 @@ func formatTxResults(txConfig client.TxConfig, resTxs []*ctypes.ResultTx, resBlo
var err error
out := make([]*sdk.TxResponse, len(resTxs))
for i := range resTxs {
out[i], err = formatTxResult(txConfig, resTxs[i], resBlocks[resTxs[i].Height])
out[i], err = mkTxResult(txConfig, resTxs[i], resBlocks[resTxs[i].Height])
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -133,27 +133,20 @@ func getBlocksForTxResults(clientCtx client.Context, resTxs []*ctypes.ResultTx)
return resBlocks, nil
}

func formatTxResult(txConfig client.TxConfig, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (*sdk.TxResponse, error) {
anyTx, err := parseTx(txConfig, resTx.Tx)
func mkTxResult(txConfig client.TxConfig, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (*sdk.TxResponse, error) {
txb, err := txConfig.TxDecoder()(resTx.Tx)
if err != nil {
return nil, err
}

return sdk.NewResponseResultTx(resTx, anyTx.AsAny(), resBlock.Block.Time.Format(time.RFC3339)), nil
}

func parseTx(txConfig client.TxConfig, txBytes []byte) (codectypes.IntoAny, error) {
var tx sdk.Tx

tx, err := txConfig.TxDecoder()(txBytes)
if err != nil {
return nil, err
}

anyTx, ok := tx.(codectypes.IntoAny)
p, ok := txb.(intoAny)
if !ok {
return nil, fmt.Errorf("tx cannot be packed into Any")
return nil, fmt.Errorf("expecting a type implementing intoAny, got: %T", txb)
}
any := p.AsAny()
return sdk.NewResponseResultTx(resTx, any, resBlock.Block.Time.Format(time.RFC3339)), nil
}

return anyTx, nil
// Deprecated:
type intoAny interface {
AsAny() *codectypes.Any
}
11 changes: 6 additions & 5 deletions x/auth/legacy/legacytx/stdtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ import (

// Interface implementation checks
var (
_ sdk.Tx = (*StdTx)(nil)
_ codectypes.IntoAny = (*StdTx)(nil)
_ sdk.TxWithMemo = (*StdTx)(nil)
_ sdk.FeeTx = (*StdTx)(nil)
_ sdk.Tx = (*StdTx)(nil)
_ sdk.TxWithMemo = (*StdTx)(nil)
_ sdk.FeeTx = (*StdTx)(nil)
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
)

// StdFee includes the amount of coins paid in fees and the maximum
Expand Down Expand Up @@ -172,7 +171,9 @@ func (tx StdTx) ValidateBasic() error {
return nil
}

// AsAny implements IntoAny.AsAny.
// Deprecated: AsAny implements intoAny. It doesn't work for protobuf serialization,
// so it can't be saved into protobuf configured storage. We are using it only for API
// compatibility.
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
func (tx *StdTx) AsAny() *codectypes.Any {
return codectypes.UnsafePackAny(tx)
}
Expand Down
3 changes: 1 addition & 2 deletions x/auth/legacy/v040/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
v040auth "github.com/cosmos/cosmos-sdk/x/auth/types"
v040vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
)
Expand All @@ -16,7 +15,7 @@ func convertBaseAccount(old *v039auth.BaseAccount) *v040auth.BaseAccount {
// just leave it nil.
if old.PubKey != nil {
var err error
any, err = tx.PubKeyToAny(old.PubKey)
any, err = codectypes.NewAnyWithValue(old.PubKey)
if err != nil {
panic(err)
}
Expand Down
22 changes: 10 additions & 12 deletions x/auth/tx/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var (
_ client.TxBuilder = &wrapper{}
_ ante.HasExtensionOptionsTx = &wrapper{}
_ ExtensionOptionsTxBuilder = &wrapper{}
_ codectypes.IntoAny = &wrapper{}
_ tx.ProtoTxProvider = &wrapper{}
)

// ExtensionOptionsTxBuilder defines a TxBuilder that can also set extensions.
Expand Down Expand Up @@ -284,7 +284,7 @@ func (w *wrapper) SetSignatures(signatures ...signing.SignatureV2) error {
for i, sig := range signatures {
var modeInfo *tx.ModeInfo
modeInfo, rawSigs[i] = SignatureDataToModeInfoAndSig(sig.Data)
any, err := PubKeyToAny(sig.PubKey)
any, err := codectypes.NewAnyWithValue(sig.PubKey)
if err != nil {
return err
}
Expand Down Expand Up @@ -315,11 +315,14 @@ func (w *wrapper) GetTx() authsigning.Tx {
return w
}

// GetProtoTx returns the tx as a proto.Message.
func (w *wrapper) AsAny() *codectypes.Any {
// We're sure here that w.tx is a proto.Message, so this will call
// codectypes.NewAnyWithValue under the hood.
return codectypes.UnsafePackAny(w.tx)
func (w *wrapper) GetProtoTx() *tx.Tx {
return w.tx
}
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved

// Deprecated: AsAny extracts proto Tx and wraps it into Any.
// NOTE: You should probably use `GetProtoTx` if you want to serialize the transaction.
func (w *wrapper) AsAny() (*codectypes.Any, error) {
return codectypes.NewAnyWithValue(w.tx)
}

// WrapTx creates a TxBuilder wrapper around a tx.Tx proto message.
Expand All @@ -346,8 +349,3 @@ func (w *wrapper) SetNonCriticalExtensionOptions(extOpts ...*codectypes.Any) {
w.tx.Body.NonCriticalExtensionOptions = extOpts
w.bodyBz = nil
}

// PubKeyToAny converts a cryptotypes.PubKey to a proto Any.
func PubKeyToAny(key cryptotypes.PubKey) (*codectypes.Any, error) {
return codectypes.NewAnyWithValue(key)
}
3 changes: 1 addition & 2 deletions x/auth/tx/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ func TestTxBuilder(t *testing.T) {
memo := "sometestmemo"
msgs := []sdk.Msg{testdata.NewTestMsg(addr)}
accSeq := uint64(2) // Arbitrary account sequence

any, err := PubKeyToAny(pubkey)
any, err := codectypes.NewAnyWithValue(pubkey)
require.NoError(t, err)

var signerInfo []*txtypes.SignerInfo
Expand Down
3 changes: 1 addition & 2 deletions x/auth/tx/direct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ func TestDirectModeHandler(t *testing.T) {
memo := "sometestmemo"
msgs := []sdk.Msg{testdata.NewTestMsg(addr)}
accSeq := uint64(2) // Arbitrary account sequence

any, err := PubKeyToAny(pubkey)
any, err := codectypes.NewAnyWithValue(pubkey)
require.NoError(t, err)

var signerInfo []*txtypes.SignerInfo
Expand Down
17 changes: 3 additions & 14 deletions x/auth/tx/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/network"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -456,20 +455,10 @@ func (s IntegrationTestSuite) mkTxBuilder() client.TxBuilder {

// txBuilderToProtoTx converts a txBuilder into a proto tx.Tx.
func txBuilderToProtoTx(txBuilder client.TxBuilder) (*tx.Tx, error) { // nolint
intoAnyTx, ok := txBuilder.(codectypes.IntoAny)
protoProvider, ok := txBuilder.(tx.ProtoTxProvider)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (codectypes.IntoAny)(nil), intoAnyTx)
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected proto tx builder, got %T", txBuilder)
}

any := intoAnyTx.AsAny().GetCachedValue()
if any == nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "any's cached value is empty")
}

protoTx, ok := any.(*tx.Tx)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (codectypes.IntoAny)(nil), intoAnyTx)
}

return protoTx, nil
return protoProvider.GetProtoTx(), nil
}
Loading