1use crate::base58::Pubkey;
37use std::collections::BTreeMap;
38
39pub type Blockhash = [u8; 32];
41
42#[derive(Clone, PartialEq, Eq, Debug)]
44pub struct AccountMeta {
45 pub pubkey: Pubkey,
46 pub is_signer: bool,
47 pub is_writable: bool,
48}
49
50impl AccountMeta {
51 pub fn signer_writable(pubkey: Pubkey) -> Self { Self { pubkey, is_signer: true, is_writable: true } }
52 pub fn signer_readonly(pubkey: Pubkey) -> Self { Self { pubkey, is_signer: true, is_writable: false } }
53 pub fn writable(pubkey: Pubkey) -> Self { Self { pubkey, is_signer: false, is_writable: true } }
54 pub fn readonly(pubkey: Pubkey) -> Self { Self { pubkey, is_signer: false, is_writable: false } }
55}
56
57#[derive(Clone, PartialEq, Eq, Debug)]
59pub struct Instruction {
60 pub program_id: Pubkey,
61 pub accounts: Vec<AccountMeta>,
62 pub data: Vec<u8>,
63}
64
65#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
67pub struct MessageHeader {
68 pub num_required_signatures: u8,
69 pub num_readonly_signed_accounts: u8,
70 pub num_readonly_unsigned_accounts: u8,
71}
72
73#[derive(Clone, PartialEq, Eq, Debug)]
75pub struct CompiledInstruction {
76 pub program_id_index: u8,
77 pub accounts: Vec<u8>,
78 pub data: Vec<u8>,
79}
80
81#[derive(Clone, PartialEq, Eq, Debug)]
84pub struct MessageAddressTableLookup {
85 pub account_key: Pubkey,
86 pub writable_indexes: Vec<u8>,
87 pub readonly_indexes: Vec<u8>,
88}
89
90#[derive(Clone, PartialEq, Eq, Debug)]
92pub struct MessageV0 {
93 pub header: MessageHeader,
94 pub account_keys: Vec<Pubkey>,
95 pub recent_blockhash: Blockhash,
96 pub instructions: Vec<CompiledInstruction>,
97 pub address_table_lookups: Vec<MessageAddressTableLookup>,
98}
99
100#[derive(Clone, PartialEq, Eq, Debug)]
103pub struct VersionedTransaction {
104 pub signatures: Vec<[u8; 64]>,
105 pub message: MessageV0,
106}
107
108#[derive(Default, Clone, Copy)]
110struct KeyMeta {
111 is_signer: bool,
112 is_writable: bool,
113}
114
115pub fn build_unsigned(ixs: &[Instruction], payer: Pubkey, blockhash: Blockhash) -> VersionedTransaction {
125 let mut key_map: BTreeMap<Pubkey, KeyMeta> = BTreeMap::new();
126
127 for ix in ixs {
131 key_map.entry(ix.program_id).or_default();
132 for am in &ix.accounts {
133 let m = key_map.entry(am.pubkey).or_default();
134 m.is_signer |= am.is_signer;
135 m.is_writable |= am.is_writable;
136 }
137 }
138
139 {
141 let m = key_map.entry(payer).or_default();
142 m.is_signer = true;
143 m.is_writable = true;
144 }
145 key_map.remove(&payer);
148
149 let writable_signers: Vec<Pubkey> = std::iter::once(payer)
150 .chain(
151 key_map
152 .iter()
153 .filter(|(_, m)| m.is_signer && m.is_writable)
154 .map(|(k, _)| *k),
155 )
156 .collect();
157 let readonly_signers: Vec<Pubkey> = key_map
158 .iter()
159 .filter(|(_, m)| m.is_signer && !m.is_writable)
160 .map(|(k, _)| *k)
161 .collect();
162 let writable_non_signers: Vec<Pubkey> = key_map
163 .iter()
164 .filter(|(_, m)| !m.is_signer && m.is_writable)
165 .map(|(k, _)| *k)
166 .collect();
167 let readonly_non_signers: Vec<Pubkey> = key_map
168 .iter()
169 .filter(|(_, m)| !m.is_signer && !m.is_writable)
170 .map(|(k, _)| *k)
171 .collect();
172
173 let account_keys: Vec<Pubkey> = writable_signers
174 .iter()
175 .chain(readonly_signers.iter())
176 .chain(writable_non_signers.iter())
177 .chain(readonly_non_signers.iter())
178 .copied()
179 .collect();
180
181 let header = MessageHeader {
182 num_required_signatures: u8::try_from(writable_signers.len() + readonly_signers.len())
183 .expect("<=255 signers"),
184 num_readonly_signed_accounts: u8::try_from(readonly_signers.len()).expect("<=255 readonly signers"),
185 num_readonly_unsigned_accounts: u8::try_from(readonly_non_signers.len())
186 .expect("<=255 readonly non-signers"),
187 };
188
189 let mut index_map: BTreeMap<Pubkey, u8> = BTreeMap::new();
191 for (i, k) in account_keys.iter().enumerate() {
192 index_map.insert(*k, u8::try_from(i).expect("<=255 account keys"));
193 }
194
195 let instructions: Vec<CompiledInstruction> = ixs
196 .iter()
197 .map(|ix| {
198 let accounts: Vec<u8> = ix
199 .accounts
200 .iter()
201 .map(|am| *index_map.get(&am.pubkey).expect("account key present in account_keys"))
202 .collect();
203 CompiledInstruction {
204 program_id_index: *index_map.get(&ix.program_id).expect("program id present in account_keys"),
205 accounts,
206 data: ix.data.clone(),
207 }
208 })
209 .collect();
210
211 let message = MessageV0 {
212 header,
213 account_keys,
214 recent_blockhash: blockhash,
215 instructions,
216 address_table_lookups: Vec::new(),
217 };
218 VersionedTransaction { signatures: Vec::new(), message }
219}
220
221pub fn serialize(tx: &VersionedTransaction) -> Vec<u8> {
226 let mut out = Vec::new();
227 encode_short_vec(tx.signatures.len(), &mut out);
228 for sig in &tx.signatures {
229 out.extend_from_slice(sig);
230 }
231 encode_v0_message(&tx.message, &mut out);
232 out
233}
234
235pub fn serialize_message(msg: &MessageV0) -> Vec<u8> {
238 let mut out = Vec::new();
239 encode_v0_message(msg, &mut out);
240 out
241}
242
243fn encode_v0_message(msg: &MessageV0, out: &mut Vec<u8>) {
244 out.push(0x80); out.push(msg.header.num_required_signatures);
247 out.push(msg.header.num_readonly_signed_accounts);
248 out.push(msg.header.num_readonly_unsigned_accounts);
249 encode_short_vec(msg.account_keys.len(), out);
251 for k in &msg.account_keys {
252 out.extend_from_slice(k.as_bytes());
253 }
254 out.extend_from_slice(&msg.recent_blockhash);
256 encode_short_vec(msg.instructions.len(), out);
258 for ci in &msg.instructions {
259 out.push(ci.program_id_index);
260 encode_short_vec(ci.accounts.len(), out);
261 out.extend_from_slice(&ci.accounts);
262 encode_short_vec(ci.data.len(), out);
263 out.extend_from_slice(&ci.data);
264 }
265 encode_short_vec(msg.address_table_lookups.len(), out);
267 for alt in &msg.address_table_lookups {
268 out.extend_from_slice(alt.account_key.as_bytes());
269 encode_short_vec(alt.writable_indexes.len(), out);
270 out.extend_from_slice(&alt.writable_indexes);
271 encode_short_vec(alt.readonly_indexes.len(), out);
272 out.extend_from_slice(&alt.readonly_indexes);
273 }
274}
275
276fn encode_short_vec(len: usize, out: &mut Vec<u8>) {
280 let len = u16::try_from(len).expect("short-vec length fits in u16 (<=65535)");
281 if len < 0x80 {
282 out.push(len as u8);
283 } else if len < 0x4000 {
284 out.push(0x80 | (len & 0x7f) as u8);
285 out.push((len >> 7) as u8);
286 } else {
287 out.push(0x80 | (len & 0x7f) as u8);
288 out.push(0x80 | ((len >> 7) & 0x7f) as u8);
289 out.push((len >> 14) as u8);
290 }
291}