cw0/
parse_reply.rs

1use thiserror::Error;
2
3use cosmwasm_std::{Binary, Reply};
4
5// Protobuf wire types (https://developers.google.com/protocol-buffers/docs/encoding)
6const WIRE_TYPE_LENGTH_DELIMITED: u8 = 2;
7// Up to 9 bytes of varints as a practical limit (https://github.com/multiformats/unsigned-varint#practical-maximum-of-9-bytes-for-security)
8const VARINT_MAX_BYTES: usize = 9;
9
10#[derive(Clone, Debug, PartialEq)]
11pub struct MsgInstantiateContractResponse {
12    pub contract_address: String,
13    pub data: Option<Binary>,
14}
15
16#[derive(Clone, Debug, PartialEq)]
17pub struct MsgExecuteContractResponse {
18    pub data: Option<Binary>,
19}
20
21/// Base128 varint decoding.
22/// The remaining of the data is kept in the data parameter.
23fn parse_protobuf_varint(data: &mut Vec<u8>, field_number: u8) -> Result<usize, ParseReplyError> {
24    let data_len = data.len();
25    let mut len: u64 = 0;
26    let mut i = 0;
27    while i < VARINT_MAX_BYTES {
28        if data_len == i {
29            return Err(ParseReplyError::ParseFailure(format!(
30                "failed to decode Protobuf message: field #{}: varint data too short",
31                field_number
32            )));
33        }
34        len += ((data[i] & 0x7f) as u64) << (i * 7);
35        if data[i] & 0x80 == 0 {
36            break;
37        }
38        i += 1;
39    }
40    if i == VARINT_MAX_BYTES {
41        return Err(ParseReplyError::ParseFailure(format!(
42            "failed to decode Protobuf message: field #{}: varint data too long",
43            field_number
44        )));
45    }
46    *data = data[i + 1..].to_owned();
47
48    Ok(len as usize) // Gently fall back to the arch's max addressable size
49}
50
51/// Helper function to parse length-prefixed protobuf fields.
52/// The remaining of the data is kept in the data parameter.
53fn parse_protobuf_length_prefixed(
54    data: &mut Vec<u8>,
55    field_number: u8,
56) -> Result<Vec<u8>, ParseReplyError> {
57    if data.is_empty() {
58        return Ok(vec![]);
59    };
60    let mut rest_1 = data.split_off(1);
61    let wire_type = data[0] & 0b11;
62    let field = data[0] >> 3;
63
64    if field != field_number {
65        return Err(ParseReplyError::ParseFailure(format!(
66            "failed to decode Protobuf message: invalid field #{} for field #{}",
67            field, field_number
68        )));
69    }
70    if wire_type != WIRE_TYPE_LENGTH_DELIMITED {
71        return Err(ParseReplyError::ParseFailure(format!(
72            "failed to decode Protobuf message: field #{}: invalid wire type {}",
73            field_number, wire_type
74        )));
75    }
76
77    let len = parse_protobuf_varint(&mut rest_1, field_number)?;
78    if rest_1.len() < len {
79        return Err(ParseReplyError::ParseFailure(format!(
80            "failed to decode Protobuf message: field #{}: message too short",
81            field_number
82        )));
83    }
84    *data = rest_1.split_off(len);
85
86    Ok(rest_1)
87}
88
89fn parse_protobuf_string(data: &mut Vec<u8>, field_number: u8) -> Result<String, ParseReplyError> {
90    let str_field = parse_protobuf_length_prefixed(data, field_number)?;
91    Ok(String::from_utf8(str_field)?)
92}
93
94fn parse_protobuf_bytes(
95    data: &mut Vec<u8>,
96    field_number: u8,
97) -> Result<Option<Binary>, ParseReplyError> {
98    let bytes_field = parse_protobuf_length_prefixed(data, field_number)?;
99    if bytes_field.is_empty() {
100        Ok(None)
101    } else {
102        Ok(Some(Binary(bytes_field)))
103    }
104}
105
106pub fn parse_reply_instantiate_data(
107    msg: Reply,
108) -> Result<MsgInstantiateContractResponse, ParseReplyError> {
109    let data = msg
110        .result
111        .into_result()
112        .map_err(ParseReplyError::SubMsgFailure)?
113        .data
114        .ok_or_else(|| ParseReplyError::ParseFailure("Missing reply data".to_owned()))?;
115    parse_instantiate_response_data(&data.0)
116}
117
118pub fn parse_reply_execute_data(msg: Reply) -> Result<MsgExecuteContractResponse, ParseReplyError> {
119    let data = msg
120        .result
121        .into_result()
122        .map_err(ParseReplyError::SubMsgFailure)?
123        .data
124        .ok_or_else(|| ParseReplyError::ParseFailure("Missing reply data".to_owned()))?;
125    parse_execute_response_data(&data.0)
126}
127
128pub fn parse_instantiate_response_data(
129    data: &[u8],
130) -> Result<MsgInstantiateContractResponse, ParseReplyError> {
131    // Manual protobuf decoding
132    let mut data = data.to_vec();
133    // Parse contract addr
134    let contract_addr = parse_protobuf_string(&mut data, 1)?;
135
136    // Parse (optional) data
137    let data = parse_protobuf_bytes(&mut data, 2)?;
138
139    Ok(MsgInstantiateContractResponse {
140        contract_address: contract_addr,
141        data,
142    })
143}
144
145pub fn parse_execute_response_data(
146    data: &[u8],
147) -> Result<MsgExecuteContractResponse, ParseReplyError> {
148    // Manual protobuf decoding
149    let mut data = data.to_vec();
150    let inner_data = parse_protobuf_bytes(&mut data, 1)?;
151
152    Ok(MsgExecuteContractResponse { data: inner_data })
153}
154
155#[derive(Error, Debug, PartialEq)]
156pub enum ParseReplyError {
157    #[error("Failure response from sub-message: {0}")]
158    SubMsgFailure(String),
159
160    #[error("Invalid reply from sub-message: {0}")]
161    ParseFailure(String),
162
163    #[error("Error occurred while converting from UTF-8")]
164    BrokenUtf8(#[from] std::string::FromUtf8Error),
165}
166
167#[cfg(test)]
168mod test {
169    use super::*;
170    use crate::parse_reply::ParseReplyError::{BrokenUtf8, ParseFailure};
171    use cosmwasm_std::{ContractResult, SubMsgExecutionResponse};
172    use prost::Message;
173    use std::str::from_utf8;
174
175    fn encode_bytes(data: &[u8]) -> Vec<u8> {
176        #[derive(Clone, PartialEq, Message)]
177        struct ProtobufBytes {
178            #[prost(bytes, tag = "1")]
179            pub data: Vec<u8>,
180        }
181
182        let data = ProtobufBytes {
183            data: data.to_vec(),
184        };
185        let mut encoded_data = Vec::<u8>::with_capacity(data.encoded_len());
186        data.encode(&mut encoded_data).unwrap();
187
188        encoded_data
189    }
190
191    fn encode_string(data: &str) -> Vec<u8> {
192        #[derive(Clone, PartialEq, Message)]
193        struct ProtobufString {
194            #[prost(string, tag = "1")]
195            pub data: String,
196        }
197
198        let data = ProtobufString {
199            data: data.to_string(),
200        };
201        let mut encoded_data = Vec::<u8>::with_capacity(data.encoded_len());
202        data.encode(&mut encoded_data).unwrap();
203
204        encoded_data
205    }
206
207    #[derive(Clone, PartialEq, Message)]
208    struct MsgInstantiateContractResponse {
209        #[prost(string, tag = "1")]
210        pub contract_address: ::prost::alloc::string::String,
211        #[prost(bytes, tag = "2")]
212        pub data: ::prost::alloc::vec::Vec<u8>,
213    }
214
215    #[derive(Clone, PartialEq, Message)]
216    struct MsgExecuteContractResponse {
217        #[prost(bytes, tag = "1")]
218        pub data: ::prost::alloc::vec::Vec<u8>,
219    }
220
221    #[test]
222    fn parse_protobuf_varint_tests() {
223        let field_number = 1;
224        // Single-byte varint works
225        let mut data = b"\x0a".to_vec();
226        let len = parse_protobuf_varint(&mut data, field_number).unwrap();
227        assert_eq!(len, 10);
228
229        // Rest is returned
230        let mut data = b"\x0a\x0b".to_vec();
231        let len = parse_protobuf_varint(&mut data, field_number).unwrap();
232        assert_eq!(len, 10);
233        assert_eq!(data, b"\x0b".to_vec());
234
235        // Multi-byte varint works
236        // 300 % 128 = 44. 44 + 128 = 172 (0xac) (1st byte)
237        // 300 / 128 = 2 (x02) (2nd byte)
238        let mut data = b"\xac\x02".to_vec();
239        let len = parse_protobuf_varint(&mut data, field_number).unwrap();
240        assert_eq!(len, 300);
241
242        // Rest is returned
243        let mut data = b"\xac\x02\x0c".to_vec();
244        let len = parse_protobuf_varint(&mut data, field_number).unwrap();
245        assert_eq!(len, 300);
246        assert_eq!(data, b"\x0c".to_vec());
247
248        // varint data too short (Empty varint)
249        let mut data = vec![];
250        let err = parse_protobuf_varint(&mut data, field_number).unwrap_err();
251        assert!(matches!(err, ParseFailure(..)));
252
253        // varint data too short (Incomplete varint)
254        let mut data = b"\x80".to_vec();
255        let err = parse_protobuf_varint(&mut data, field_number).unwrap_err();
256        assert!(matches!(err, ParseFailure(..)));
257
258        // varint data too long
259        let mut data = b"\x80\x81\x82\x83\x84\x83\x82\x81\x80".to_vec();
260        let err = parse_protobuf_varint(&mut data, field_number).unwrap_err();
261        assert!(matches!(err, ParseFailure(..)));
262    }
263
264    #[test]
265    fn parse_protobuf_length_prefixed_tests() {
266        let field_number = 1;
267        // Single-byte length-prefixed works
268        let mut data = b"\x0a\x03abc".to_vec();
269        let res = parse_protobuf_length_prefixed(&mut data, field_number).unwrap();
270        assert_eq!(res, b"abc".to_vec());
271        assert_eq!(data, vec![0u8; 0]);
272
273        // Rest is returned
274        let mut data = b"\x0a\x03abcd".to_vec();
275        let res = parse_protobuf_length_prefixed(&mut data, field_number).unwrap();
276        assert_eq!(res, b"abc".to_vec());
277        assert_eq!(data, b"d".to_vec());
278
279        // Multi-byte length-prefixed works
280        let mut data = [b"\x0a\xac\x02", vec![65u8; 300].as_slice()]
281            .concat()
282            .to_vec();
283        let res = parse_protobuf_length_prefixed(&mut data, field_number).unwrap();
284        assert_eq!(res, vec![65u8; 300]);
285        assert_eq!(data, vec![0u8; 0]);
286
287        // Rest is returned
288        let mut data = [b"\x0a\xac\x02", vec![65u8; 300].as_slice(), b"rest"]
289            .concat()
290            .to_vec();
291        let res = parse_protobuf_length_prefixed(&mut data, field_number).unwrap();
292        assert_eq!(res, vec![65u8; 300]);
293        assert_eq!(data, b"rest");
294
295        // message too short
296        let mut data = b"\x0a\x01".to_vec();
297        let field_number = 1;
298        let err = parse_protobuf_length_prefixed(&mut data, field_number).unwrap_err();
299        assert!(matches!(err, ParseFailure(..)));
300
301        // invalid wire type
302        let mut data = b"\x0b\x01a".to_vec();
303        let err = parse_protobuf_length_prefixed(&mut data, field_number).unwrap_err();
304        assert!(matches!(err, ParseFailure(..)));
305
306        // invalid field number
307        let field_number = 2;
308        let mut data = b"\x0a\x01a".to_vec();
309        let err = parse_protobuf_length_prefixed(&mut data, field_number).unwrap_err();
310        assert!(matches!(err, ParseFailure(..)));
311    }
312
313    #[test]
314    fn parse_protobuf_bytes_works() {
315        let field_number = 1;
316
317        // Empty works
318        let data = vec![];
319        let mut encoded_data = encode_bytes(&data);
320
321        let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
322        assert_eq!(res, None);
323
324        // Simple works
325        let data = b"test".to_vec();
326        let mut encoded_data = encode_bytes(&data);
327
328        let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
329        assert_eq!(res, Some(Binary(data)));
330
331        // Large works
332        let data = vec![0x40; 300];
333        let mut encoded_data = encode_bytes(&data);
334
335        let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
336        assert_eq!(res, Some(Binary(data)));
337
338        // Field number works
339        let field_number = 5;
340        let data = b"test field 5".to_vec();
341        let mut encoded_data = encode_bytes(&data);
342        encoded_data[0] = (field_number << 3) + WIRE_TYPE_LENGTH_DELIMITED;
343
344        let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
345        assert_eq!(res, Some(Binary(data)));
346
347        // Remainder is kept
348        let field_number = 1;
349        let test_len: usize = 4;
350        let data = b"test_remainder".to_vec();
351        let mut encoded_data = encode_bytes(&data);
352        encoded_data[1] = test_len as u8;
353
354        let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
355        assert_eq!(res, Some(Binary(data[..test_len].to_owned())));
356        assert_eq!(encoded_data, data[test_len..].to_owned());
357    }
358
359    #[test]
360    fn parse_protobuf_string_tests() {
361        let field_number = 1;
362
363        // Empty works
364        let data = "";
365        let mut encoded_data = encode_string(data);
366
367        let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
368        assert_eq!(res, data);
369
370        // Simple works
371        let data = "test";
372        let mut encoded_data = encode_string(data);
373
374        let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
375        assert_eq!(res, data);
376
377        // Large works
378        let data = vec![0x40; 300];
379        let str_data = from_utf8(data.as_slice()).unwrap();
380        let mut encoded_data = encode_string(str_data);
381
382        let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
383        assert_eq!(res, str_data);
384
385        // Field number works
386        let field_number = 5;
387        let data = "test field 5";
388        let mut encoded_data = encode_string(data);
389        encoded_data[0] = (field_number << 3) + WIRE_TYPE_LENGTH_DELIMITED;
390
391        let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
392        assert_eq!(res, data);
393
394        // Remainder is kept
395        let field_number = 1;
396        let test_len: usize = 4;
397        let data = "test_remainder";
398        let mut encoded_data = encode_string(data);
399        encoded_data[1] = test_len as u8;
400
401        let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
402        assert_eq!(res, data[..test_len]);
403        assert_eq!(encoded_data, data[test_len..].as_bytes());
404
405        // Broken utf-8 errs
406        let field_number = 1;
407        let data = "test_X";
408        let mut encoded_data = encode_string(data);
409        let encoded_len = encoded_data.len();
410        encoded_data[encoded_len - 1] = 0xd3;
411        let err = parse_protobuf_string(&mut encoded_data, field_number).unwrap_err();
412        assert!(matches!(err, BrokenUtf8(..)));
413    }
414
415    #[test]
416    fn parse_reply_instantiate_data_works() {
417        let contract_addr: &str = "Contract #1";
418        for (data, expected) in [
419            (
420                vec![],
421                super::MsgInstantiateContractResponse {
422                    contract_address: contract_addr.to_string(),
423                    data: None,
424                },
425            ),
426            (
427                vec![1u8, 2, 255, 7, 5],
428                super::MsgInstantiateContractResponse {
429                    contract_address: contract_addr.to_string(),
430                    data: Some(Binary(vec![1u8, 2, 255, 7, 5])),
431                },
432            ),
433            (
434                vec![1u8; 127],
435                super::MsgInstantiateContractResponse {
436                    contract_address: contract_addr.to_string(),
437                    data: Some(Binary(vec![1u8; 127])),
438                },
439            ),
440            (
441                vec![2u8; 128],
442                super::MsgInstantiateContractResponse {
443                    contract_address: contract_addr.to_string(),
444                    data: Some(Binary(vec![2u8; 128])),
445                },
446            ),
447            (
448                vec![3u8; 257],
449                super::MsgInstantiateContractResponse {
450                    contract_address: contract_addr.to_string(),
451                    data: Some(Binary(vec![3u8; 257])),
452                },
453            ),
454        ] {
455            let instantiate_reply = MsgInstantiateContractResponse {
456                contract_address: contract_addr.to_string(),
457                data,
458            };
459            let mut encoded_instantiate_reply =
460                Vec::<u8>::with_capacity(instantiate_reply.encoded_len());
461            // The data must encode successfully
462            instantiate_reply
463                .encode(&mut encoded_instantiate_reply)
464                .unwrap();
465
466            // Build reply message
467            let msg = Reply {
468                id: 1,
469                result: ContractResult::Ok(SubMsgExecutionResponse {
470                    events: vec![],
471                    data: Some(encoded_instantiate_reply.into()),
472                }),
473            };
474
475            let res = parse_reply_instantiate_data(msg).unwrap();
476            assert_eq!(res, expected);
477        }
478    }
479
480    #[test]
481    fn parse_reply_execute_data_works() {
482        for (data, expected) in [
483            (vec![], super::MsgExecuteContractResponse { data: None }),
484            (
485                vec![1u8, 2, 3, 127, 15],
486                super::MsgExecuteContractResponse {
487                    data: Some(Binary(vec![1u8, 2, 3, 127, 15])),
488                },
489            ),
490            (
491                vec![0u8; 255],
492                super::MsgExecuteContractResponse {
493                    data: Some(Binary(vec![0u8; 255])),
494                },
495            ),
496            (
497                vec![1u8; 256],
498                super::MsgExecuteContractResponse {
499                    data: Some(Binary(vec![1u8; 256])),
500                },
501            ),
502            (
503                vec![2u8; 32769],
504                super::MsgExecuteContractResponse {
505                    data: Some(Binary(vec![2u8; 32769])),
506                },
507            ),
508        ] {
509            let execute_reply = MsgExecuteContractResponse { data };
510            let mut encoded_execute_reply = Vec::<u8>::with_capacity(execute_reply.encoded_len());
511            // The data must encode successfully
512            execute_reply.encode(&mut encoded_execute_reply).unwrap();
513
514            // Build reply message
515            let msg = Reply {
516                id: 1,
517                result: ContractResult::Ok(SubMsgExecutionResponse {
518                    events: vec![],
519                    data: Some(encoded_execute_reply.into()),
520                }),
521            };
522
523            let res = parse_reply_execute_data(msg).unwrap();
524
525            assert_eq!(res, expected);
526        }
527    }
528}