diff --git a/x/tx/CHANGELOG.md b/x/tx/CHANGELOG.md index 182c6dbfefb5..1cc2b29455b8 100644 --- a/x/tx/CHANGELOG.md +++ b/x/tx/CHANGELOG.md @@ -36,6 +36,7 @@ Since v0.13.0, x/tx follows Cosmos SDK semver: https://github.com/cosmos/cosmos- * [#21782](https://github.com/cosmos/cosmos-sdk/pull/21782) Fix JSON attribute sort order on messages with oneof fields. * [#21825](https://github.com/cosmos/cosmos-sdk/pull/21825) Fix decimal encoding and field ordering in Amino JSON encoder. * [#21850](https://github.com/cosmos/cosmos-sdk/pull/21850) Support bytes field as signer. +* [#22311](https://github.com/cosmos/cosmos-sdk/pull/22311) Fix add feePayer as signer. ## [v0.13.5](https://github.com/cosmos/cosmos-sdk/releases/tag/x/tx/v0.13.5) - 2024-09-18 diff --git a/x/tx/decode/decode.go b/x/tx/decode/decode.go index bf4f3a54f31f..81d0385784c8 100644 --- a/x/tx/decode/decode.go +++ b/x/tx/decode/decode.go @@ -167,6 +167,18 @@ func (d *Decoder) Decode(txBytes []byte) (*DecodedTx, error) { } } + // If a fee payer is specified in the AuthInfo, it must be added to the list of signers + if authInfo.Fee != nil && authInfo.Fee.Payer != "" { + feeAddr, err := d.signingCtx.AddressCodec().StringToBytes(authInfo.Fee.Payer) + if err != nil { + return nil, errorsmod.Wrap(ErrTxDecode, err.Error()) + } + + if _, seen := seenSigners[string(feeAddr)]; !seen { + signers = append(signers, feeAddr) + } + } + return &DecodedTx{ Messages: msgs, DynamicMessages: dynamicMsgs, diff --git a/x/tx/decode/decode_test.go b/x/tx/decode/decode_test.go index 60be25387640..2fe9a5255b53 100644 --- a/x/tx/decode/decode_test.go +++ b/x/tx/decode/decode_test.go @@ -61,19 +61,42 @@ func TestDecode(t *testing.T) { gogoproto.RegisterType(&testpb.A{}, string((&testpb.A{}).ProtoReflect().Descriptor().FullName())) testCases := []struct { - name string - msg proto.Message - error string + name string + msg proto.Message + feePayer string + error string + expectedSigners int }{ { - name: "happy path", - msg: &bankv1beta1.MsgSend{}, + name: "happy path", + msg: &bankv1beta1.MsgSend{}, + expectedSigners: 1, }, { name: "empty signer option", msg: &testpb.A{}, error: "no cosmos.msg.v1.signer option found for message A; use DefineCustomGetSigners to specify a custom getter: tx parse error", }, + { + name: "invalid feePayer", + msg: &bankv1beta1.MsgSend{}, + feePayer: "payer", + error: `encoding/hex: invalid byte: U+0070 'p': tx parse error`, + }, + { + name: "valid feePayer", + msg: &bankv1beta1.MsgSend{}, + feePayer: "636f736d6f733168363935356b3836397a72306770383975717034337a373263393033666d35647a366b75306c", // hexadecimal to work with dummyAddressCodec + expectedSigners: 2, + }, + { + name: "same msg signer and feePayer", + msg: &bankv1beta1.MsgSend{ + FromAddress: "636f736d6f733168363935356b3836397a72306770383975717034337a373263393033666d35647a366b75306c", + }, + feePayer: "636f736d6f733168363935356b3836397a72306770383975717034337a373263393033666d35647a366b75306c", + expectedSigners: 1, + }, } for _, tc := range testCases { @@ -94,7 +117,7 @@ func TestDecode(t *testing.T) { Fee: &txv1beta1.Fee{ Amount: []*basev1beta1.Coin{{Amount: "100", Denom: "denom"}}, GasLimit: 100, - Payer: "payer", + Payer: tc.feePayer, Granter: "", }, }, @@ -109,6 +132,7 @@ func TestDecode(t *testing.T) { return } require.NoError(t, err) + require.Equal(t, len(decodeTx.Signers), tc.expectedSigners) require.Equal(t, fmt.Sprintf("/%s", tc.msg.ProtoReflect().Descriptor().FullName()),