tonlib_core/wallet/versioned/
v5.rs1use crate::cell::{ArcCell, CellBuilder, CellParser, TonCellError};
2use crate::tlb_types::block::out_action::{OutAction, OutActionSendMsg, OutList};
3use crate::tlb_types::primitives::reference::Ref;
4use crate::tlb_types::tlb::{TLBPrefix, TLB};
5use crate::types::TonHash;
6use crate::wallet::versioned::utils::validate_msgs_count;
7
8#[derive(Debug, PartialEq, Clone)]
11pub struct WalletDataV5 {
12 pub signature_allowed: bool,
13 pub seqno: u32,
14 pub wallet_id: i32,
15 pub public_key: TonHash,
16 pub extensions: Option<Ref<ArcCell>>,
17}
18
19#[derive(Debug, PartialEq, Clone)]
24pub struct WalletExtMsgBodyV5 {
25 pub wallet_id: i32,
26 pub valid_until: u32,
27 pub msg_seqno: u32,
28 pub msgs_modes: Vec<u8>,
29 pub msgs: Vec<ArcCell>,
30}
31
32impl WalletDataV5 {
33 pub fn new(wallet_id: i32, public_key: TonHash) -> Self {
34 Self {
35 signature_allowed: true,
36 seqno: 0,
37 wallet_id,
38 public_key,
39 extensions: None,
40 }
41 }
42}
43
44impl TLB for WalletDataV5 {
45 fn read_definition(parser: &mut CellParser) -> Result<Self, TonCellError> {
46 Ok(Self {
47 signature_allowed: parser.load_bit()?,
48 seqno: parser.load_u32(32)?,
49 wallet_id: parser.load_i32(32)?,
50 public_key: parser.load_tonhash()?,
51 extensions: TLB::read(parser)?,
52 })
53 }
54
55 fn write_definition(&self, dst: &mut CellBuilder) -> Result<(), TonCellError> {
56 dst.store_bit(self.signature_allowed)?;
57 dst.store_u32(32, self.seqno)?;
58 dst.store_i32(32, self.wallet_id)?;
59 dst.store_tonhash(&self.public_key)?;
60 self.extensions.write(dst)?;
61 Ok(())
62 }
63}
64
65impl TLB for WalletExtMsgBodyV5 {
66 const PREFIX: TLBPrefix = TLBPrefix::new(32, 0x7369676e);
67 fn read_definition(parser: &mut CellParser) -> Result<Self, TonCellError> {
68 let wallet_id = parser.load_i32(32)?;
69 let valid_until = parser.load_u32(32)?;
70 let msg_seqno = parser.load_u32(32)?;
71 let inner_request = InnerRequest::read(parser)?;
72 let (msgs, msgs_modes) = parse_inner_request(inner_request)?;
73 Ok(Self {
74 wallet_id,
75 valid_until,
76 msg_seqno,
77 msgs_modes,
78 msgs,
79 })
80 }
81
82 fn write_definition(&self, dst: &mut CellBuilder) -> Result<(), TonCellError> {
83 dst.store_i32(32, self.wallet_id)?;
84 dst.store_u32(32, self.valid_until)?;
85 dst.store_u32(32, self.msg_seqno)?;
86 let inner_req = build_inner_request(&self.msgs, &self.msgs_modes)?;
87 inner_req.write(dst)?;
88 Ok(())
89 }
90}
91
92#[derive(Debug, PartialEq, Clone)]
94pub(super) struct InnerRequest {
95 out_actions: Option<Ref<OutList>>, }
98
99impl TLB for InnerRequest {
100 fn read_definition(parser: &mut CellParser) -> Result<Self, TonCellError> {
101 let out_actions = TLB::read(parser)?;
102 if parser.load_bit()? {
103 return Err(TonCellError::InternalError(
104 "other_actions parsing is unsupported".to_string(),
105 ));
106 }
107 Ok(Self { out_actions })
108 }
109
110 fn write_definition(&self, dst: &mut CellBuilder) -> Result<(), TonCellError> {
111 self.out_actions.write(dst)?;
112 dst.store_bit(false)?; Ok(())
114 }
115}
116
117fn parse_inner_request(request: InnerRequest) -> Result<(Vec<ArcCell>, Vec<u8>), TonCellError> {
118 let mut out_list = match request.out_actions {
119 Some(out_list) => out_list.0,
120 None => return Ok((vec![], vec![])),
121 };
122 let mut msgs = vec![];
123 let mut msgs_modes = vec![];
124 while let OutList::Some(action) = out_list {
125 if let OutAction::SendMsg(action_send_msg) = &action.action {
126 msgs.push(action_send_msg.out_msg.clone());
127 msgs_modes.push(action_send_msg.mode);
128 } else {
129 let err_str = format!("Unsupported OutAction: {action:?}");
130 return Err(TonCellError::InvalidCellData(err_str));
131 }
132 out_list = TLB::from_cell(&action.prev.0)?;
133 }
134
135 Ok((msgs, msgs_modes))
136}
137
138fn build_inner_request(msgs: &[ArcCell], msgs_modes: &[u8]) -> Result<InnerRequest, TonCellError> {
139 validate_msgs_count(msgs, msgs_modes, 255)?;
140 let mut actions = vec![];
142 for (msg, mode) in msgs.iter().zip(msgs_modes.iter()) {
143 let action = OutActionSendMsg {
144 mode: *mode,
145 out_msg: msg.clone(),
146 };
147 actions.push(OutAction::SendMsg(action));
148 }
149
150 let out_list = OutList::new(&actions)?;
151
152 let req = InnerRequest {
153 out_actions: Some(Ref::new(out_list)),
154 };
155 Ok(req)
156}
157
158#[cfg(test)]
159mod test {
160 use super::*;
161 use crate::cell::Cell;
162 use crate::tlb_types::tlb::TLB;
163 use crate::wallet::versioned::{DEFAULT_WALLET_ID_V5R1, DEFAULT_WALLET_ID_V5R1_TESTNET};
164
165 #[test]
166 fn test_wallet_data_v5() -> anyhow::Result<()> {
167 let src_boc_hex = "b5ee9c7241010101002b00005180000000bfffff88e5f9bbe4db9b026385fb9a446ee75d0a7bb1dd77956387b468eb01950900a4fa20cbe13a2a";
169 let wallet_data = WalletDataV5::from_boc_hex(src_boc_hex)?;
170 assert_eq!(wallet_data.seqno, 1);
171 assert_eq!(wallet_data.wallet_id, DEFAULT_WALLET_ID_V5R1);
172 assert_eq!(
173 wallet_data.public_key,
174 TonHash::from_hex("cbf377c9b73604c70bf73488ddceba14f763baef2ac70f68d1d6032a120149f4")?
175 );
176 assert_eq!(wallet_data.extensions, None);
177
178 let serial_boc_hex = wallet_data.to_boc_hex(true)?;
179 assert_eq!(src_boc_hex, serial_boc_hex);
180 let restored = WalletDataV5::from_boc_hex(&serial_boc_hex)?;
181 assert_eq!(wallet_data, restored);
182 Ok(())
183 }
184
185 #[test]
186 fn test_wallet_data_v5_testnet() -> anyhow::Result<()> {
187 let src_boc_hex = "b5ee9c7201010101002b000051800000013ffffffed2b31b23dbe5144a626b9d5d1d4208e36d97e4adb472d42c073bfff85b3107e4a0";
188 let wallet_data = WalletDataV5::from_boc_hex(src_boc_hex)?;
189 assert_eq!(wallet_data.seqno, 2);
190 assert_eq!(wallet_data.wallet_id, DEFAULT_WALLET_ID_V5R1_TESTNET);
191 Ok(())
192 }
193
194 #[test]
195 fn test_wallet_ext_msg_body_v5() -> anyhow::Result<()> {
196 let body_hex = "b5ee9c720101040100940001a17369676e7fffff11ffffffff00000000bc04889cb28b36a3a00810e363a413763ec34860bf0fce552c5d36e37289fafd442f1983d740f92378919d969dd530aec92d258a0779fb371d4659f10ca1b3826001020a0ec3c86d030302006642007847b4630eb08d9f486fe846d5496878556dfd5a084f82a9a3fb01224e67c84c187a1200000000000000000000000000000000";
198 let body_cell = Cell::from_boc_hex(body_hex)?;
199 let mut body_parser = body_cell.parser();
200 let body = WalletExtMsgBodyV5::read(&mut body_parser)?;
201 let sign = body_parser.load_bytes(64)?;
202
203 assert_eq!(body.wallet_id, DEFAULT_WALLET_ID_V5R1);
204 assert_eq!(body.valid_until, 4294967295);
205 assert_eq!(body.msg_seqno, 0);
206 assert_eq!(body.msgs_modes, vec![3]);
207 assert_eq!(body.msgs.len(), 1);
208
209 let serial_cell = body.to_cell()?;
210 let signed_serial = CellBuilder::new()
211 .store_cell(&serial_cell)?
212 .store_slice(&sign)?
213 .build()?;
214
215 assert_eq!(body_cell, signed_serial);
216 let parsed_back = WalletExtMsgBodyV5::from_cell(&signed_serial)?;
217 assert_eq!(body, parsed_back);
218 Ok(())
219 }
220}