1use crate::crypto::utils::sha256;
2use crate::error::{HiveError, Result};
3use crate::serialization::types::{
4 write_array, write_asset, write_authority, write_bool, write_chain_properties, write_date,
5 write_flat_map, write_i16, write_i64, write_optional, write_price, write_public_key,
6 write_string, write_u16, write_u32, write_u64, write_variable_binary, write_varint32,
7 write_void_array,
8};
9use crate::types::{
10 AccountCreateOperation, AccountCreateWithDelegationOperation, AccountUpdate2Operation,
11 AccountUpdateOperation, AccountWitnessProxyOperation, AccountWitnessVoteOperation,
12 CancelTransferFromSavingsOperation, ChainId, ChangeRecoveryAccountOperation,
13 ClaimAccountOperation, ClaimRewardBalanceOperation, CollateralizedConvertOperation,
14 CommentOperation, CommentOptionsExtension, CommentOptionsOperation, ConvertOperation,
15 CreateClaimedAccountOperation, CreateProposalOperation, CustomBinaryOperation,
16 CustomJsonOperation, CustomOperation, DeclineVotingRightsOperation,
17 DelegateVestingSharesOperation, DeleteCommentOperation, EscrowApproveOperation,
18 EscrowDisputeOperation, EscrowReleaseOperation, EscrowTransferOperation, FeedPublishOperation,
19 LimitOrderCancelOperation, LimitOrderCreate2Operation, LimitOrderCreateOperation, Operation,
20 Pow2Operation, PowOperation, RecoverAccountOperation, RecurrentTransferOperation,
21 RemoveProposalOperation, ReportOverProductionOperation, RequestAccountRecoveryOperation,
22 ResetAccountOperation, SetResetAccountOperation, SetWithdrawVestingRouteOperation,
23 SignedBlockHeader, Transaction, TransferFromSavingsOperation, TransferOperation,
24 TransferToSavingsOperation, TransferToVestingOperation, UpdateProposalExtension,
25 UpdateProposalOperation, UpdateProposalVotesOperation, VoteOperation, WithdrawVestingOperation,
26 WitnessSetPropertiesOperation, WitnessUpdateOperation,
27};
28
29pub trait HiveSerialize {
30 fn hive_serialize(&self, buf: &mut Vec<u8>) -> Result<()>;
31}
32
33impl HiveSerialize for Operation {
34 fn hive_serialize(&self, buf: &mut Vec<u8>) -> Result<()> {
35 write_varint32(buf, self.id() as u32);
36 match self {
37 Operation::Vote(op) => serialize_vote(buf, op),
38 Operation::Comment(op) => serialize_comment(buf, op),
39 Operation::Transfer(op) => serialize_transfer(buf, op),
40 Operation::TransferToVesting(op) => serialize_transfer_to_vesting(buf, op),
41 Operation::WithdrawVesting(op) => serialize_withdraw_vesting(buf, op),
42 Operation::LimitOrderCreate(op) => serialize_limit_order_create(buf, op),
43 Operation::LimitOrderCancel(op) => serialize_limit_order_cancel(buf, op),
44 Operation::FeedPublish(op) => serialize_feed_publish(buf, op),
45 Operation::Convert(op) => serialize_convert(buf, op),
46 Operation::AccountCreate(op) => serialize_account_create(buf, op),
47 Operation::AccountUpdate(op) => serialize_account_update(buf, op),
48 Operation::WitnessUpdate(op) => serialize_witness_update(buf, op),
49 Operation::AccountWitnessVote(op) => serialize_account_witness_vote(buf, op),
50 Operation::AccountWitnessProxy(op) => serialize_account_witness_proxy(buf, op),
51 Operation::Pow(op) => serialize_pow(buf, op),
52 Operation::Custom(op) => serialize_custom(buf, op),
53 Operation::ReportOverProduction(op) => serialize_report_over_production(buf, op),
54 Operation::DeleteComment(op) => serialize_delete_comment(buf, op),
55 Operation::CustomJson(op) => serialize_custom_json(buf, op),
56 Operation::CommentOptions(op) => serialize_comment_options(buf, op),
57 Operation::SetWithdrawVestingRoute(op) => serialize_set_withdraw_vesting_route(buf, op),
58 Operation::LimitOrderCreate2(op) => serialize_limit_order_create2(buf, op),
59 Operation::ClaimAccount(op) => serialize_claim_account(buf, op),
60 Operation::CreateClaimedAccount(op) => serialize_create_claimed_account(buf, op),
61 Operation::RequestAccountRecovery(op) => serialize_request_account_recovery(buf, op),
62 Operation::RecoverAccount(op) => serialize_recover_account(buf, op),
63 Operation::ChangeRecoveryAccount(op) => serialize_change_recovery_account(buf, op),
64 Operation::EscrowTransfer(op) => serialize_escrow_transfer(buf, op),
65 Operation::EscrowDispute(op) => serialize_escrow_dispute(buf, op),
66 Operation::EscrowRelease(op) => serialize_escrow_release(buf, op),
67 Operation::Pow2(op) => serialize_pow2(buf, op),
68 Operation::EscrowApprove(op) => serialize_escrow_approve(buf, op),
69 Operation::TransferToSavings(op) => serialize_transfer_to_savings(buf, op),
70 Operation::TransferFromSavings(op) => serialize_transfer_from_savings(buf, op),
71 Operation::CancelTransferFromSavings(op) => {
72 serialize_cancel_transfer_from_savings(buf, op)
73 }
74 Operation::CustomBinary(op) => serialize_custom_binary(buf, op),
75 Operation::DeclineVotingRights(op) => serialize_decline_voting_rights(buf, op),
76 Operation::ResetAccount(op) => serialize_reset_account(buf, op),
77 Operation::SetResetAccount(op) => serialize_set_reset_account(buf, op),
78 Operation::ClaimRewardBalance(op) => serialize_claim_reward_balance(buf, op),
79 Operation::DelegateVestingShares(op) => serialize_delegate_vesting_shares(buf, op),
80 Operation::AccountCreateWithDelegation(op) => {
81 serialize_account_create_with_delegation(buf, op)
82 }
83 Operation::WitnessSetProperties(op) => serialize_witness_set_properties(buf, op),
84 Operation::AccountUpdate2(op) => serialize_account_update2(buf, op),
85 Operation::CreateProposal(op) => serialize_create_proposal(buf, op),
86 Operation::UpdateProposalVotes(op) => serialize_update_proposal_votes(buf, op),
87 Operation::RemoveProposal(op) => serialize_remove_proposal(buf, op),
88 Operation::UpdateProposal(op) => serialize_update_proposal(buf, op),
89 Operation::CollateralizedConvert(op) => serialize_collateralized_convert(buf, op),
90 Operation::RecurrentTransfer(op) => serialize_recurrent_transfer(buf, op),
91 }
92 }
93}
94
95impl HiveSerialize for Transaction {
96 fn hive_serialize(&self, buf: &mut Vec<u8>) -> Result<()> {
97 write_u16(buf, self.ref_block_num);
98 write_u32(buf, self.ref_block_prefix);
99 write_date(buf, &self.expiration)?;
100 write_array(buf, &self.operations, |b, op| op.hive_serialize(b))?;
101 write_array(buf, &self.extensions, |b, ext| {
102 write_string(b, ext);
103 Ok(())
104 })?;
105 Ok(())
106 }
107}
108
109pub fn serialize_transaction(transaction: &Transaction) -> Result<Vec<u8>> {
110 let mut buf = Vec::new();
111 transaction.hive_serialize(&mut buf)?;
112 Ok(buf)
113}
114
115pub fn transaction_digest(transaction: &Transaction, chain_id: &ChainId) -> Result<[u8; 32]> {
116 let tx_bytes = serialize_transaction(transaction)?;
117 let mut to_hash = Vec::with_capacity(chain_id.bytes.len() + tx_bytes.len());
118 to_hash.extend_from_slice(&chain_id.bytes);
119 to_hash.extend_from_slice(&tx_bytes);
120 Ok(sha256(&to_hash))
121}
122
123pub fn generate_trx_id(transaction: &Transaction) -> Result<String> {
124 let tx_bytes = serialize_transaction(transaction)?;
125 let hash = sha256(&tx_bytes);
126 Ok(hex::encode(hash)[..40].to_string())
127}
128
129fn write_void_extensions(buf: &mut Vec<u8>, extensions: &[()]) -> Result<()> {
130 if !extensions.is_empty() {
131 return Err(HiveError::Serialization(
132 "void extensions must be empty".to_string(),
133 ));
134 }
135 write_void_array(buf);
136 Ok(())
137}
138
139fn write_fixed_binary_hex(buf: &mut Vec<u8>, hex_value: &str, expected_len: usize) -> Result<()> {
140 let bytes = hex::decode(hex_value).map_err(|err| {
141 HiveError::Serialization(format!("invalid hex field '{hex_value}': {err}"))
142 })?;
143 if bytes.len() != expected_len {
144 return Err(HiveError::Serialization(format!(
145 "expected {expected_len} bytes, got {}",
146 bytes.len()
147 )));
148 }
149 buf.extend_from_slice(&bytes);
150 Ok(())
151}
152
153fn write_signed_block_header(buf: &mut Vec<u8>, header: &SignedBlockHeader) -> Result<()> {
154 write_fixed_binary_hex(buf, &header.header.previous, 20)?;
155 write_date(buf, &header.header.timestamp)?;
156 write_string(buf, &header.header.witness);
157 write_fixed_binary_hex(buf, &header.header.transaction_merkle_root, 20)?;
158 if !header.header.extensions.is_empty() {
159 return Err(HiveError::Serialization(
160 "signed block header extensions are expected to be empty".to_string(),
161 ));
162 }
163 write_void_array(buf);
164 write_fixed_binary_hex(buf, &header.witness_signature, 65)
165}
166
167fn serialize_vote(buf: &mut Vec<u8>, op: &VoteOperation) -> Result<()> {
168 write_string(buf, &op.voter);
169 write_string(buf, &op.author);
170 write_string(buf, &op.permlink);
171 write_i16(buf, op.weight);
172 Ok(())
173}
174
175fn serialize_comment(buf: &mut Vec<u8>, op: &CommentOperation) -> Result<()> {
176 write_string(buf, &op.parent_author);
177 write_string(buf, &op.parent_permlink);
178 write_string(buf, &op.author);
179 write_string(buf, &op.permlink);
180 write_string(buf, &op.title);
181 write_string(buf, &op.body);
182 write_string(buf, &op.json_metadata);
183 Ok(())
184}
185
186fn serialize_transfer(buf: &mut Vec<u8>, op: &TransferOperation) -> Result<()> {
187 write_string(buf, &op.from);
188 write_string(buf, &op.to);
189 write_asset(buf, &op.amount)?;
190 write_string(buf, &op.memo);
191 Ok(())
192}
193
194fn serialize_transfer_to_vesting(buf: &mut Vec<u8>, op: &TransferToVestingOperation) -> Result<()> {
195 write_string(buf, &op.from);
196 write_string(buf, &op.to);
197 write_asset(buf, &op.amount)
198}
199
200fn serialize_withdraw_vesting(buf: &mut Vec<u8>, op: &WithdrawVestingOperation) -> Result<()> {
201 write_string(buf, &op.account);
202 write_asset(buf, &op.vesting_shares)
203}
204
205fn serialize_limit_order_create(buf: &mut Vec<u8>, op: &LimitOrderCreateOperation) -> Result<()> {
206 write_string(buf, &op.owner);
207 write_u32(buf, op.orderid);
208 write_asset(buf, &op.amount_to_sell)?;
209 write_asset(buf, &op.min_to_receive)?;
210 write_bool(buf, op.fill_or_kill);
211 write_date(buf, &op.expiration)
212}
213
214fn serialize_limit_order_cancel(buf: &mut Vec<u8>, op: &LimitOrderCancelOperation) -> Result<()> {
215 write_string(buf, &op.owner);
216 write_u32(buf, op.orderid);
217 Ok(())
218}
219
220fn serialize_feed_publish(buf: &mut Vec<u8>, op: &FeedPublishOperation) -> Result<()> {
221 write_string(buf, &op.publisher);
222 write_price(buf, &op.exchange_rate)
223}
224
225fn serialize_convert(buf: &mut Vec<u8>, op: &ConvertOperation) -> Result<()> {
226 write_string(buf, &op.owner);
227 write_u32(buf, op.requestid);
228 write_asset(buf, &op.amount)
229}
230
231fn serialize_account_create(buf: &mut Vec<u8>, op: &AccountCreateOperation) -> Result<()> {
232 write_asset(buf, &op.fee)?;
233 write_string(buf, &op.creator);
234 write_string(buf, &op.new_account_name);
235 write_authority(buf, &op.owner)?;
236 write_authority(buf, &op.active)?;
237 write_authority(buf, &op.posting)?;
238 write_public_key(buf, &op.memo_key)?;
239 write_string(buf, &op.json_metadata);
240 Ok(())
241}
242
243fn serialize_account_update(buf: &mut Vec<u8>, op: &AccountUpdateOperation) -> Result<()> {
244 write_string(buf, &op.account);
245 write_optional(buf, op.owner.as_ref(), write_authority)?;
246 write_optional(buf, op.active.as_ref(), write_authority)?;
247 write_optional(buf, op.posting.as_ref(), write_authority)?;
248 write_public_key(buf, &op.memo_key)?;
249 write_string(buf, &op.json_metadata);
250 Ok(())
251}
252
253fn serialize_witness_update(buf: &mut Vec<u8>, op: &WitnessUpdateOperation) -> Result<()> {
254 write_string(buf, &op.owner);
255 write_string(buf, &op.url);
256 write_public_key(buf, &op.block_signing_key)?;
257 write_chain_properties(buf, &op.props)?;
258 write_asset(buf, &op.fee)
259}
260
261fn serialize_account_witness_vote(
262 buf: &mut Vec<u8>,
263 op: &AccountWitnessVoteOperation,
264) -> Result<()> {
265 write_string(buf, &op.account);
266 write_string(buf, &op.witness);
267 write_bool(buf, op.approve);
268 Ok(())
269}
270
271fn serialize_account_witness_proxy(
272 buf: &mut Vec<u8>,
273 op: &AccountWitnessProxyOperation,
274) -> Result<()> {
275 write_string(buf, &op.account);
276 write_string(buf, &op.proxy);
277 Ok(())
278}
279
280fn serialize_pow(_buf: &mut Vec<u8>, _op: &PowOperation) -> Result<()> {
281 Err(HiveError::Serialization(
282 "pow operation serialization is unsupported".to_string(),
283 ))
284}
285
286fn serialize_custom(buf: &mut Vec<u8>, op: &CustomOperation) -> Result<()> {
287 write_array(buf, &op.required_auths, |b, auth| {
288 write_string(b, auth);
289 Ok(())
290 })?;
291 write_u16(buf, op.id);
292 write_variable_binary(buf, &op.data);
293 Ok(())
294}
295
296fn serialize_report_over_production(
297 buf: &mut Vec<u8>,
298 op: &ReportOverProductionOperation,
299) -> Result<()> {
300 write_string(buf, &op.reporter);
301 write_signed_block_header(buf, &op.first_block)?;
302 write_signed_block_header(buf, &op.second_block)?;
303 Ok(())
304}
305
306fn serialize_delete_comment(buf: &mut Vec<u8>, op: &DeleteCommentOperation) -> Result<()> {
307 write_string(buf, &op.author);
308 write_string(buf, &op.permlink);
309 Ok(())
310}
311
312fn serialize_custom_json(buf: &mut Vec<u8>, op: &CustomJsonOperation) -> Result<()> {
313 write_array(buf, &op.required_auths, |b, auth| {
314 write_string(b, auth);
315 Ok(())
316 })?;
317 write_array(buf, &op.required_posting_auths, |b, auth| {
318 write_string(b, auth);
319 Ok(())
320 })?;
321 write_string(buf, &op.id);
322 write_string(buf, &op.json);
323 Ok(())
324}
325
326fn serialize_comment_options(buf: &mut Vec<u8>, op: &CommentOptionsOperation) -> Result<()> {
327 write_string(buf, &op.author);
328 write_string(buf, &op.permlink);
329 write_asset(buf, &op.max_accepted_payout)?;
330 write_u16(buf, op.percent_hbd);
331 write_bool(buf, op.allow_votes);
332 write_bool(buf, op.allow_curation_rewards);
333 write_array(buf, &op.extensions, |b, ext| match ext {
334 CommentOptionsExtension::Beneficiaries { beneficiaries } => {
335 write_varint32(b, 0);
336 write_array(b, beneficiaries, |bb, route| {
337 write_string(bb, &route.account);
338 write_u16(bb, route.weight);
339 Ok(())
340 })
341 }
342 })?;
343 Ok(())
344}
345
346fn serialize_set_withdraw_vesting_route(
347 buf: &mut Vec<u8>,
348 op: &SetWithdrawVestingRouteOperation,
349) -> Result<()> {
350 write_string(buf, &op.from_account);
351 write_string(buf, &op.to_account);
352 write_u16(buf, op.percent);
353 write_bool(buf, op.auto_vest);
354 Ok(())
355}
356
357fn serialize_limit_order_create2(buf: &mut Vec<u8>, op: &LimitOrderCreate2Operation) -> Result<()> {
358 write_string(buf, &op.owner);
359 write_u32(buf, op.orderid);
360 write_asset(buf, &op.amount_to_sell)?;
361 write_price(buf, &op.exchange_rate)?;
362 write_bool(buf, op.fill_or_kill);
363 write_date(buf, &op.expiration)
364}
365
366fn serialize_claim_account(buf: &mut Vec<u8>, op: &ClaimAccountOperation) -> Result<()> {
367 write_string(buf, &op.creator);
368 write_asset(buf, &op.fee)?;
369 write_void_extensions(buf, &op.extensions)
370}
371
372fn serialize_create_claimed_account(
373 buf: &mut Vec<u8>,
374 op: &CreateClaimedAccountOperation,
375) -> Result<()> {
376 write_string(buf, &op.creator);
377 write_string(buf, &op.new_account_name);
378 write_authority(buf, &op.owner)?;
379 write_authority(buf, &op.active)?;
380 write_authority(buf, &op.posting)?;
381 write_public_key(buf, &op.memo_key)?;
382 write_string(buf, &op.json_metadata);
383 write_void_extensions(buf, &op.extensions)
384}
385
386fn serialize_request_account_recovery(
387 buf: &mut Vec<u8>,
388 op: &RequestAccountRecoveryOperation,
389) -> Result<()> {
390 write_string(buf, &op.recovery_account);
391 write_string(buf, &op.account_to_recover);
392 write_authority(buf, &op.new_owner_authority)?;
393 write_void_extensions(buf, &op.extensions)
394}
395
396fn serialize_recover_account(buf: &mut Vec<u8>, op: &RecoverAccountOperation) -> Result<()> {
397 write_string(buf, &op.account_to_recover);
398 write_authority(buf, &op.new_owner_authority)?;
399 write_authority(buf, &op.recent_owner_authority)?;
400 write_void_extensions(buf, &op.extensions)
401}
402
403fn serialize_change_recovery_account(
404 buf: &mut Vec<u8>,
405 op: &ChangeRecoveryAccountOperation,
406) -> Result<()> {
407 write_string(buf, &op.account_to_recover);
408 write_string(buf, &op.new_recovery_account);
409 write_void_extensions(buf, &op.extensions)
410}
411
412fn serialize_escrow_transfer(buf: &mut Vec<u8>, op: &EscrowTransferOperation) -> Result<()> {
413 write_string(buf, &op.from);
414 write_string(buf, &op.to);
415 write_asset(buf, &op.hbd_amount)?;
416 write_asset(buf, &op.hive_amount)?;
417 write_u32(buf, op.escrow_id);
418 write_string(buf, &op.agent);
419 write_asset(buf, &op.fee)?;
420 write_string(buf, &op.json_meta);
421 write_date(buf, &op.ratification_deadline)?;
422 write_date(buf, &op.escrow_expiration)
423}
424
425fn serialize_escrow_dispute(buf: &mut Vec<u8>, op: &EscrowDisputeOperation) -> Result<()> {
426 write_string(buf, &op.from);
427 write_string(buf, &op.to);
428 write_string(buf, &op.agent);
429 write_string(buf, &op.who);
430 write_u32(buf, op.escrow_id);
431 Ok(())
432}
433
434fn serialize_escrow_release(buf: &mut Vec<u8>, op: &EscrowReleaseOperation) -> Result<()> {
435 write_string(buf, &op.from);
436 write_string(buf, &op.to);
437 write_string(buf, &op.agent);
438 write_string(buf, &op.who);
439 write_string(buf, &op.receiver);
440 write_u32(buf, op.escrow_id);
441 write_asset(buf, &op.hbd_amount)?;
442 write_asset(buf, &op.hive_amount)?;
443 Ok(())
444}
445
446fn serialize_pow2(_buf: &mut Vec<u8>, _op: &Pow2Operation) -> Result<()> {
447 Err(HiveError::Serialization(
448 "pow2 operation serialization is unsupported".to_string(),
449 ))
450}
451
452fn serialize_escrow_approve(buf: &mut Vec<u8>, op: &EscrowApproveOperation) -> Result<()> {
453 write_string(buf, &op.from);
454 write_string(buf, &op.to);
455 write_string(buf, &op.agent);
456 write_string(buf, &op.who);
457 write_u32(buf, op.escrow_id);
458 write_bool(buf, op.approve);
459 Ok(())
460}
461
462fn serialize_transfer_to_savings(buf: &mut Vec<u8>, op: &TransferToSavingsOperation) -> Result<()> {
463 write_string(buf, &op.from);
464 write_string(buf, &op.to);
465 write_asset(buf, &op.amount)?;
466 write_string(buf, &op.memo);
467 Ok(())
468}
469
470fn serialize_transfer_from_savings(
471 buf: &mut Vec<u8>,
472 op: &TransferFromSavingsOperation,
473) -> Result<()> {
474 write_string(buf, &op.from);
475 write_u32(buf, op.request_id);
476 write_string(buf, &op.to);
477 write_asset(buf, &op.amount)?;
478 write_string(buf, &op.memo);
479 Ok(())
480}
481
482fn serialize_cancel_transfer_from_savings(
483 buf: &mut Vec<u8>,
484 op: &CancelTransferFromSavingsOperation,
485) -> Result<()> {
486 write_string(buf, &op.from);
487 write_u32(buf, op.request_id);
488 Ok(())
489}
490
491fn serialize_custom_binary(buf: &mut Vec<u8>, op: &CustomBinaryOperation) -> Result<()> {
492 write_array(buf, &op.required_owner_auths, |b, value| {
493 write_string(b, value);
494 Ok(())
495 })?;
496 write_array(buf, &op.required_active_auths, |b, value| {
497 write_string(b, value);
498 Ok(())
499 })?;
500 write_array(buf, &op.required_posting_auths, |b, value| {
501 write_string(b, value);
502 Ok(())
503 })?;
504 write_array(buf, &op.required_auths, write_authority)?;
505 write_string(buf, &op.id);
506 write_variable_binary(buf, &op.data);
507 Ok(())
508}
509
510fn serialize_decline_voting_rights(
511 buf: &mut Vec<u8>,
512 op: &DeclineVotingRightsOperation,
513) -> Result<()> {
514 write_string(buf, &op.account);
515 write_bool(buf, op.decline);
516 Ok(())
517}
518
519fn serialize_reset_account(buf: &mut Vec<u8>, op: &ResetAccountOperation) -> Result<()> {
520 write_string(buf, &op.reset_account);
521 write_string(buf, &op.account_to_reset);
522 write_authority(buf, &op.new_owner_authority)
523}
524
525fn serialize_set_reset_account(buf: &mut Vec<u8>, op: &SetResetAccountOperation) -> Result<()> {
526 write_string(buf, &op.account);
527 write_string(buf, &op.current_reset_account);
528 write_string(buf, &op.reset_account);
529 Ok(())
530}
531
532fn serialize_claim_reward_balance(
533 buf: &mut Vec<u8>,
534 op: &ClaimRewardBalanceOperation,
535) -> Result<()> {
536 write_string(buf, &op.account);
537 write_asset(buf, &op.reward_hive)?;
538 write_asset(buf, &op.reward_hbd)?;
539 write_asset(buf, &op.reward_vests)
540}
541
542fn serialize_delegate_vesting_shares(
543 buf: &mut Vec<u8>,
544 op: &DelegateVestingSharesOperation,
545) -> Result<()> {
546 write_string(buf, &op.delegator);
547 write_string(buf, &op.delegatee);
548 write_asset(buf, &op.vesting_shares)
549}
550
551fn serialize_account_create_with_delegation(
552 buf: &mut Vec<u8>,
553 op: &AccountCreateWithDelegationOperation,
554) -> Result<()> {
555 write_asset(buf, &op.fee)?;
556 write_asset(buf, &op.delegation)?;
557 write_string(buf, &op.creator);
558 write_string(buf, &op.new_account_name);
559 write_authority(buf, &op.owner)?;
560 write_authority(buf, &op.active)?;
561 write_authority(buf, &op.posting)?;
562 write_public_key(buf, &op.memo_key)?;
563 write_string(buf, &op.json_metadata);
564 write_void_extensions(buf, &op.extensions)
565}
566
567fn serialize_witness_set_properties(
568 buf: &mut Vec<u8>,
569 op: &WitnessSetPropertiesOperation,
570) -> Result<()> {
571 write_string(buf, &op.owner);
572 let mut props = op.props.clone();
573 props.sort_by(|a, b| a.0.cmp(&b.0));
574 write_flat_map(
575 buf,
576 &props,
577 |b, key| {
578 write_string(b, key);
579 Ok(())
580 },
581 |b, value| {
582 write_variable_binary(b, value);
583 Ok(())
584 },
585 )?;
586 write_void_extensions(buf, &op.extensions)
587}
588
589fn serialize_account_update2(buf: &mut Vec<u8>, op: &AccountUpdate2Operation) -> Result<()> {
590 write_string(buf, &op.account);
591 write_optional(buf, op.owner.as_ref(), write_authority)?;
592 write_optional(buf, op.active.as_ref(), write_authority)?;
593 write_optional(buf, op.posting.as_ref(), write_authority)?;
594 write_optional(buf, op.memo_key.as_ref(), |b, key| write_public_key(b, key))?;
595 write_string(buf, &op.json_metadata);
596 write_string(buf, &op.posting_json_metadata);
597 write_void_extensions(buf, &op.extensions)
598}
599
600fn serialize_create_proposal(buf: &mut Vec<u8>, op: &CreateProposalOperation) -> Result<()> {
601 write_string(buf, &op.creator);
602 write_string(buf, &op.receiver);
603 write_date(buf, &op.start_date)?;
604 write_date(buf, &op.end_date)?;
605 write_asset(buf, &op.daily_pay)?;
606 write_string(buf, &op.subject);
607 write_string(buf, &op.permlink);
608 write_void_extensions(buf, &op.extensions)
609}
610
611fn serialize_update_proposal_votes(
612 buf: &mut Vec<u8>,
613 op: &UpdateProposalVotesOperation,
614) -> Result<()> {
615 write_string(buf, &op.voter);
616 write_array(buf, &op.proposal_ids, |b, id| {
617 write_i64(b, *id);
618 Ok(())
619 })?;
620 write_bool(buf, op.approve);
621 write_void_extensions(buf, &op.extensions)
622}
623
624fn serialize_remove_proposal(buf: &mut Vec<u8>, op: &RemoveProposalOperation) -> Result<()> {
625 write_string(buf, &op.proposal_owner);
626 write_array(buf, &op.proposal_ids, |b, id| {
627 write_i64(b, *id);
628 Ok(())
629 })?;
630 write_void_extensions(buf, &op.extensions)
631}
632
633fn serialize_update_proposal(buf: &mut Vec<u8>, op: &UpdateProposalOperation) -> Result<()> {
634 write_u64(buf, op.proposal_id);
635 write_string(buf, &op.creator);
636 write_asset(buf, &op.daily_pay)?;
637 write_string(buf, &op.subject);
638 write_string(buf, &op.permlink);
639 write_array(buf, &op.extensions, |b, ext| match ext {
640 UpdateProposalExtension::Void => {
641 write_varint32(b, 0);
642 Ok(())
643 }
644 UpdateProposalExtension::EndDate { end_date } => {
645 write_varint32(b, 1);
646 write_date(b, end_date)
647 }
648 })?;
649 Ok(())
650}
651
652fn serialize_collateralized_convert(
653 buf: &mut Vec<u8>,
654 op: &CollateralizedConvertOperation,
655) -> Result<()> {
656 write_string(buf, &op.owner);
657 write_u32(buf, op.requestid);
658 write_asset(buf, &op.amount)
659}
660
661fn serialize_recurrent_transfer(buf: &mut Vec<u8>, op: &RecurrentTransferOperation) -> Result<()> {
662 write_string(buf, &op.from);
663 write_string(buf, &op.to);
664 write_asset(buf, &op.amount)?;
665 write_string(buf, &op.memo);
666 write_u16(buf, op.recurrence);
667 write_u16(buf, op.executions);
668 write_void_extensions(buf, &op.extensions)
669}
670
671#[cfg(test)]
672mod tests {
673 use crate::serialization::serializer::{
674 generate_trx_id, serialize_transaction, transaction_digest, HiveSerialize,
675 };
676 use crate::types::Asset;
677 use crate::types::{ChainId, Operation, Transaction, TransferOperation, VoteOperation};
678
679 #[test]
680 fn transfer_operation_matches_dhive_vector() {
681 let operation = Operation::Transfer(TransferOperation {
682 from: "foo".to_string(),
683 to: "bar".to_string(),
684 amount: Asset::from_string("1.000 STEEM").expect("asset should parse"),
685 memo: "wedding present".to_string(),
686 });
687
688 let mut buf = Vec::new();
689 operation
690 .hive_serialize(&mut buf)
691 .expect("operation should serialize");
692 assert_eq!(
693 hex::encode(buf),
694 "0203666f6f03626172e80300000000000003535445454d00000f77656464696e672070726573656e74"
695 );
696 }
697
698 #[test]
699 fn transaction_serialization_matches_dhive_vector() {
700 let tx = Transaction {
701 ref_block_num: 1234,
702 ref_block_prefix: 1122334455,
703 expiration: "2017-07-15T16:51:19".to_string(),
704 operations: vec![Operation::Vote(VoteOperation {
705 voter: "foo".to_string(),
706 author: "bar".to_string(),
707 permlink: "baz".to_string(),
708 weight: 10000,
709 })],
710 extensions: vec!["long-pants".to_string()],
711 };
712
713 let bytes = serialize_transaction(&tx).expect("transaction should serialize");
714 assert_eq!(
715 hex::encode(bytes),
716 "d204f776e54207486a59010003666f6f036261720362617a1027010a6c6f6e672d70616e7473"
717 );
718 }
719
720 #[test]
721 fn digest_and_id_match_dhive_vectors() {
722 let tx = Transaction {
723 ref_block_num: 1234,
724 ref_block_prefix: 1122334455,
725 expiration: "2017-07-15T16:51:19".to_string(),
726 operations: vec![Operation::Vote(VoteOperation {
727 voter: "foo".to_string(),
728 author: "bar".to_string(),
729 permlink: "baz".to_string(),
730 weight: 10000,
731 })],
732 extensions: vec!["long-pants".to_string()],
733 };
734
735 let chain_id = ChainId { bytes: [0_u8; 32] };
736 let digest = transaction_digest(&tx, &chain_id).expect("digest should compute");
737 assert_eq!(
738 hex::encode(digest),
739 "77342bdde45a4901a0a65a98e0806a292ccfeb8b9b048d1ca93af69434c866de"
740 );
741
742 let trx_id = generate_trx_id(&tx).expect("trx id should compute");
743 assert_eq!(trx_id, "70a8b9bd8e4a1413eb807f030fa8e81f9c7bb615");
744 }
745}