11package types
22
33import (
4+ "encoding/hex"
45 "strconv"
56 "strings"
67
78 sdk "github.com/cosmos/cosmos-sdk/types"
89
10+ errorsmod "cosmossdk.io/errors"
11+
912 channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types"
1013 porttypes "github.com/cosmos/ibc-go/v10/modules/core/05-port/types"
1114 ibcexported "github.com/cosmos/ibc-go/v10/modules/core/exported"
@@ -59,6 +62,9 @@ type CallbackData struct {
5962 CommitGasLimit uint64
6063 // ApplicationVersion is the base application version.
6164 ApplicationVersion string
65+ // Calldata is the calldata to be passed to the callback actor.
66+ // This may be empty but if it is not empty, it should be the calldata sent to the callback actor.
67+ Calldata []byte
6268}
6369
6470// GetSourceCallbackData parses the packet data and returns the source callback data.
@@ -114,9 +120,9 @@ func GetCallbackData(
114120 }
115121
116122 // get the callback address from the callback data
117- callbackAddress := getCallbackAddress (callbackData )
118- if strings .TrimSpace (callbackAddress ) == "" {
119- return CallbackData {}, true , ErrCallbackAddressNotFound
123+ callbackAddress , err := getCallbackAddress (callbackData )
124+ if err != nil || strings .TrimSpace (callbackAddress ) == "" {
125+ return CallbackData {}, true , ErrInvalidCallbackData
120126 }
121127
122128 // retrieve packet sender from packet data if possible and if needed
@@ -129,20 +135,32 @@ func GetCallbackData(
129135 }
130136
131137 // get the gas limit from the callback data
132- executionGasLimit , commitGasLimit := computeExecAndCommitGasLimit (callbackData , remainingGas , maxGas )
138+ executionGasLimit , commitGasLimit , err := computeExecAndCommitGasLimit (callbackData , remainingGas , maxGas )
139+ if err != nil {
140+ return CallbackData {}, true , err
141+ }
142+
143+ callData , err := getCalldata (callbackData )
144+ if err != nil {
145+ return CallbackData {}, true , err
146+ }
133147
134148 return CallbackData {
135149 CallbackAddress : callbackAddress ,
136150 ExecutionGasLimit : executionGasLimit ,
137151 SenderAddress : packetSender ,
138152 CommitGasLimit : commitGasLimit ,
139153 ApplicationVersion : version ,
154+ Calldata : callData ,
140155 }, true , nil
141156}
142157
143- func computeExecAndCommitGasLimit (callbackData map [string ]any , remainingGas , maxGas uint64 ) (uint64 , uint64 ) {
158+ func computeExecAndCommitGasLimit (callbackData map [string ]any , remainingGas , maxGas uint64 ) (uint64 , uint64 , error ) {
144159 // get the gas limit from the callback data
145- commitGasLimit := getUserDefinedGasLimit (callbackData )
160+ commitGasLimit , err := getUserDefinedGasLimit (callbackData )
161+ if err != nil {
162+ return 0 , 0 , err
163+ }
146164
147165 // ensure user defined gas limit does not exceed the max gas limit
148166 if commitGasLimit == 0 || commitGasLimit > maxGas {
@@ -153,7 +171,7 @@ func computeExecAndCommitGasLimit(callbackData map[string]any, remainingGas, max
153171 // in this case, the callback execution may be retried upon failure
154172 executionGasLimit := min (remainingGas , commitGasLimit )
155173
156- return executionGasLimit , commitGasLimit
174+ return executionGasLimit , commitGasLimit , nil
157175}
158176
159177// getUserDefinedGasLimit returns the custom gas limit provided for callbacks if it is
@@ -164,19 +182,26 @@ func computeExecAndCommitGasLimit(callbackData map[string]any, remainingGas, max
164182// { "{callbackKey}": { ... , "gas_limit": {stringForCallback} }
165183//
166184// Note: the user defined gas limit must be set as a string and not a json number.
167- func getUserDefinedGasLimit (callbackData map [string ]any ) uint64 {
185+ func getUserDefinedGasLimit (callbackData map [string ]any ) ( uint64 , error ) {
168186 // the gas limit must be specified as a string and not a json number
169- gasLimit , ok := callbackData [UserDefinedGasLimitKey ].( string )
187+ gasLimit , ok := callbackData [UserDefinedGasLimitKey ]
170188 if ! ok {
171- return 0
189+ return 0 , nil
190+ }
191+ gasLimitStr , ok := gasLimit .(string )
192+ if ! ok {
193+ return 0 , errorsmod .Wrapf (ErrInvalidCallbackData , "gas limit [%v] must be a string" , gasLimit )
194+ }
195+ if gasLimitStr == "" {
196+ return 0 , nil
172197 }
173198
174- userGas , err := strconv .ParseUint (gasLimit , 10 , 64 )
199+ userGas , err := strconv .ParseUint (gasLimitStr , 10 , 64 )
175200 if err != nil {
176- return 0
201+ return 0 , errorsmod . Wrapf ( ErrInvalidCallbackData , "gas limit must be a valid uint64: %s" , err )
177202 }
178203
179- return userGas
204+ return userGas , nil
180205}
181206
182207// getCallbackAddress returns the callback address if it is specified in the callback data.
@@ -188,13 +213,34 @@ func getUserDefinedGasLimit(callbackData map[string]any) uint64 {
188213//
189214// ADR-8 middleware should callback on the returned address if it is a PacketActor
190215// (i.e. smart contract that accepts IBC callbacks).
191- func getCallbackAddress (callbackData map [string ]any ) string {
216+ func getCallbackAddress (callbackData map [string ]any ) ( string , error ) {
192217 callbackAddress , ok := callbackData [CallbackAddressKey ].(string )
193218 if ! ok {
194- return ""
219+ return "" , errorsmod . Wrapf ( ErrInvalidCallbackData , "callback address must be a string" )
195220 }
196221
197- return callbackAddress
222+ return callbackAddress , nil
223+ }
224+
225+ // getCalldata returns the calldata if it is specified in the callback data.
226+ func getCalldata (callbackData map [string ]any ) ([]byte , error ) {
227+ calldataAny , ok := callbackData [CalldataKey ]
228+ if ! ok {
229+ return nil , nil
230+ }
231+ calldataStr , ok := calldataAny .(string )
232+ if ! ok {
233+ return nil , errorsmod .Wrapf (ErrInvalidCallbackData , "calldata must be a string" )
234+ }
235+ if calldataStr == "" {
236+ return nil , nil
237+ }
238+
239+ calldata , err := hex .DecodeString (calldataStr )
240+ if err != nil {
241+ return nil , errorsmod .Wrapf (ErrInvalidCallbackData , "calldata must be a valid hex string: %s" , err )
242+ }
243+ return calldata , nil
198244}
199245
200246// AllowRetry returns true if the callback execution gas limit is less than the commit gas limit.
0 commit comments