Skip to main content

hive_rs/serialization/
serializer.rs

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}