use super::writer::BinaryWriter;
use sha2::{Digest, Sha256};
pub mod signature_types {
pub const UNKNOWN: u64 = 0;
pub const LEGACY_ED25519: u64 = 1;
pub const ED25519: u64 = 2;
pub const RCD1: u64 = 3;
pub const BTC: u64 = 4;
pub const BTC_LEGACY: u64 = 5;
pub const ETH: u64 = 6;
pub const DELEGATED: u64 = 7;
pub const INTERNAL: u64 = 8;
pub const RSA_SHA256: u64 = 9;
pub const ECDSA_SHA256: u64 = 10;
pub const TYPED_DATA: u64 = 11;
pub const REMOTE: u64 = 12;
pub const RECEIPT: u64 = 13;
pub const PARTITION: u64 = 14;
pub const SET: u64 = 15;
pub const AUTHORITY: u64 = 16;
}
pub mod tx_types {
pub const CREATE_IDENTITY: u64 = 0x01;
pub const CREATE_TOKEN_ACCOUNT: u64 = 0x02;
pub const SEND_TOKENS: u64 = 0x03;
pub const CREATE_DATA_ACCOUNT: u64 = 0x04;
pub const WRITE_DATA: u64 = 0x05;
pub const WRITE_DATA_TO: u64 = 0x06;
pub const ACME_FAUCET: u64 = 0x07;
pub const CREATE_TOKEN: u64 = 0x08;
pub const ISSUE_TOKENS: u64 = 0x09;
pub const BURN_TOKENS: u64 = 0x0A;
pub const CREATE_LITE_TOKEN_ACCOUNT: u64 = 0x0B;
pub const CREATE_KEY_PAGE: u64 = 0x0C;
pub const CREATE_KEY_BOOK: u64 = 0x0D;
pub const ADD_CREDITS: u64 = 0x0E;
pub const UPDATE_KEY_PAGE: u64 = 0x0F;
pub const LOCK_ACCOUNT: u64 = 0x10;
pub const BURN_CREDITS: u64 = 0x11;
pub const TRANSFER_CREDITS: u64 = 0x12;
pub const UPDATE_ACCOUNT_AUTH: u64 = 0x15;
pub const UPDATE_KEY: u64 = 0x16;
}
pub mod key_page_op_types {
pub const UNKNOWN: u64 = 0;
pub const UPDATE: u64 = 1; pub const REMOVE: u64 = 2; pub const ADD: u64 = 3; pub const SET_THRESHOLD: u64 = 4; pub const UPDATE_ALLOWED: u64 = 5; pub const SET_REJECT_THRESHOLD: u64 = 6; pub const SET_RESPONSE_THRESHOLD: u64 = 7; }
pub fn compute_ed25519_signature_metadata_hash(
public_key: &[u8],
signer: &str,
signer_version: u64,
timestamp: u64,
) -> [u8; 32] {
compute_signature_metadata_hash(
signature_types::ED25519,
public_key,
signer,
signer_version,
timestamp,
0, None, None, )
}
pub fn compute_signature_metadata_hash(
signature_type: u64,
public_key: &[u8],
signer: &str,
signer_version: u64,
timestamp: u64,
vote: u64,
memo: Option<&str>,
data: Option<&[u8]>,
) -> [u8; 32] {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(signature_type);
if !public_key.is_empty() {
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(public_key.len() as u64);
let _ = writer.write_bytes(public_key);
}
if !signer.is_empty() {
let _ = writer.write_uvarint(4);
let signer_bytes = signer.as_bytes();
let _ = writer.write_uvarint(signer_bytes.len() as u64);
let _ = writer.write_bytes(signer_bytes);
}
if signer_version != 0 {
let _ = writer.write_uvarint(5);
let _ = writer.write_uvarint(signer_version);
}
if timestamp != 0 {
let _ = writer.write_uvarint(6);
let _ = writer.write_uvarint(timestamp);
}
if vote != 0 {
let _ = writer.write_uvarint(7);
let _ = writer.write_uvarint(vote);
}
if let Some(memo_str) = memo {
if !memo_str.is_empty() {
let _ = writer.write_uvarint(9);
let memo_bytes = memo_str.as_bytes();
let _ = writer.write_uvarint(memo_bytes.len() as u64);
let _ = writer.write_bytes(memo_bytes);
}
}
if let Some(data_bytes) = data {
if !data_bytes.is_empty() {
let _ = writer.write_uvarint(10);
let _ = writer.write_uvarint(data_bytes.len() as u64);
let _ = writer.write_bytes(data_bytes);
}
}
sha256_bytes(writer.bytes())
}
#[derive(Debug, Clone, Default)]
pub struct HeaderBinaryOptions {
pub expire_at_time: Option<i64>,
pub hold_until_minor_block: Option<u64>,
pub authorities: Option<Vec<String>>,
}
pub fn marshal_transaction_header(
principal: &str,
initiator: &[u8; 32],
memo: Option<&str>,
metadata: Option<&[u8]>,
) -> Vec<u8> {
marshal_transaction_header_full(principal, initiator, memo, metadata, None)
}
pub fn marshal_transaction_header_full(
principal: &str,
initiator: &[u8; 32],
memo: Option<&str>,
metadata: Option<&[u8]>,
extended: Option<&HeaderBinaryOptions>,
) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let principal_bytes = principal.as_bytes();
let _ = writer.write_uvarint(principal_bytes.len() as u64);
let _ = writer.write_bytes(principal_bytes);
let is_zero = initiator.iter().all(|&b| b == 0);
if !is_zero {
let _ = writer.write_uvarint(2);
let _ = writer.write_bytes(initiator);
}
if let Some(memo_str) = memo {
if !memo_str.is_empty() {
let _ = writer.write_uvarint(3);
let memo_bytes = memo_str.as_bytes();
let _ = writer.write_uvarint(memo_bytes.len() as u64);
let _ = writer.write_bytes(memo_bytes);
}
}
if let Some(metadata_bytes) = metadata {
if !metadata_bytes.is_empty() {
let _ = writer.write_uvarint(4);
let _ = writer.write_uvarint(metadata_bytes.len() as u64);
let _ = writer.write_bytes(metadata_bytes);
}
}
if let Some(ext) = extended {
if let Some(at_time) = ext.expire_at_time {
let mut expire_writer = BinaryWriter::new();
let _ = expire_writer.write_uvarint(1); let _ = expire_writer.write_varint(at_time); let expire_bytes = expire_writer.into_bytes();
let _ = writer.write_uvarint(5);
let _ = writer.write_uvarint(expire_bytes.len() as u64);
let _ = writer.write_bytes(&expire_bytes);
}
if let Some(minor_block) = ext.hold_until_minor_block {
let mut hold_writer = BinaryWriter::new();
let _ = hold_writer.write_uvarint(1); let _ = hold_writer.write_uvarint(minor_block);
let hold_bytes = hold_writer.into_bytes();
let _ = writer.write_uvarint(6);
let _ = writer.write_uvarint(hold_bytes.len() as u64);
let _ = writer.write_bytes(&hold_bytes);
}
if let Some(ref authorities) = ext.authorities {
for auth_url in authorities {
let _ = writer.write_uvarint(7);
let auth_bytes = auth_url.as_bytes();
let _ = writer.write_uvarint(auth_bytes.len() as u64);
let _ = writer.write_bytes(auth_bytes);
}
}
}
writer.into_bytes()
}
pub fn marshal_add_credits_body(
recipient: &str,
amount: u64,
oracle: u64,
) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::ADD_CREDITS);
let _ = writer.write_uvarint(2);
let recipient_bytes = recipient.as_bytes();
let _ = writer.write_uvarint(recipient_bytes.len() as u64);
let _ = writer.write_bytes(recipient_bytes);
if amount > 0 {
let _ = writer.write_uvarint(3);
let amount_bytes = amount_to_bigint_bytes(amount);
let _ = writer.write_uvarint(amount_bytes.len() as u64);
let _ = writer.write_bytes(&amount_bytes);
}
if oracle > 0 {
let _ = writer.write_uvarint(4);
let _ = writer.write_uvarint(oracle);
}
writer.into_bytes()
}
pub fn marshal_send_tokens_body(
recipients: &[(String, u64)], ) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::SEND_TOKENS);
for (url, amount) in recipients {
let recipient_bytes = marshal_token_recipient(url, *amount);
let _ = writer.write_uvarint(4);
let _ = writer.write_uvarint(recipient_bytes.len() as u64);
let _ = writer.write_bytes(&recipient_bytes);
}
writer.into_bytes()
}
pub fn marshal_create_identity_body(
url: &str,
key_hash: &[u8],
key_book_url: &str,
) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::CREATE_IDENTITY);
let _ = writer.write_uvarint(2);
let url_bytes = url.as_bytes();
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
if !key_hash.is_empty() {
let _ = writer.write_uvarint(3);
let _ = writer.write_uvarint(key_hash.len() as u64);
let _ = writer.write_bytes(key_hash);
}
let _ = writer.write_uvarint(4);
let book_bytes = key_book_url.as_bytes();
let _ = writer.write_uvarint(book_bytes.len() as u64);
let _ = writer.write_bytes(book_bytes);
writer.into_bytes()
}
pub fn marshal_create_data_account_body(url: &str) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::CREATE_DATA_ACCOUNT);
if !url.is_empty() {
let _ = writer.write_uvarint(2);
let url_bytes = url.as_bytes();
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
}
writer.into_bytes()
}
pub fn marshal_write_data_body(entries_hex: &[String], scratch: bool, write_to_state: bool) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::WRITE_DATA);
if !entries_hex.is_empty() {
let entry_bytes = marshal_data_entry(entries_hex);
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(entry_bytes.len() as u64);
let _ = writer.write_bytes(&entry_bytes);
}
if scratch {
let _ = writer.write_uvarint(3);
let _ = writer.write_uvarint(1); }
if write_to_state {
let _ = writer.write_uvarint(4);
let _ = writer.write_uvarint(1); }
writer.into_bytes()
}
fn marshal_data_entry(entries_hex: &[String]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(3);
for entry_hex in entries_hex {
if let Ok(data) = hex::decode(entry_hex) {
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(data.len() as u64);
let _ = writer.write_bytes(&data);
}
}
writer.into_bytes()
}
pub fn marshal_create_token_account_body(
url: &str,
token_url: &str,
) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::CREATE_TOKEN_ACCOUNT);
if !url.is_empty() {
let _ = writer.write_uvarint(2);
let url_bytes = url.as_bytes();
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
}
if !token_url.is_empty() {
let _ = writer.write_uvarint(3);
let token_bytes = token_url.as_bytes();
let _ = writer.write_uvarint(token_bytes.len() as u64);
let _ = writer.write_bytes(token_bytes);
}
writer.into_bytes()
}
fn marshal_token_recipient(url: &str, amount: u64) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let url_bytes = url.as_bytes();
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
if amount > 0 {
let _ = writer.write_uvarint(2);
let amount_bytes = amount_to_bigint_bytes(amount);
let _ = writer.write_uvarint(amount_bytes.len() as u64);
let _ = writer.write_bytes(&amount_bytes);
}
writer.into_bytes()
}
fn amount_to_bigint_bytes(mut value: u64) -> Vec<u8> {
if value == 0 {
return vec![];
}
let mut bytes = Vec::new();
while value > 0 {
bytes.push((value & 0xFF) as u8);
value >>= 8;
}
bytes.reverse(); bytes
}
pub fn marshal_create_token_body(url: &str, symbol: &str, precision: u64, supply_limit: Option<u64>) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::CREATE_TOKEN);
if !url.is_empty() {
let _ = writer.write_uvarint(2);
let url_bytes = url.as_bytes();
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
}
if !symbol.is_empty() {
let _ = writer.write_uvarint(4);
let symbol_bytes = symbol.as_bytes();
let _ = writer.write_uvarint(symbol_bytes.len() as u64);
let _ = writer.write_bytes(symbol_bytes);
}
let _ = writer.write_uvarint(5);
let _ = writer.write_uvarint(precision);
if let Some(limit) = supply_limit {
if limit > 0 {
let _ = writer.write_uvarint(7);
let limit_bytes = amount_to_bigint_bytes(limit);
let _ = writer.write_uvarint(limit_bytes.len() as u64);
let _ = writer.write_bytes(&limit_bytes);
}
}
writer.into_bytes()
}
pub fn marshal_issue_tokens_body(recipients: &[(&str, u64)]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::ISSUE_TOKENS);
for (url, amount) in recipients {
let recipient_bytes = marshal_token_recipient(url, *amount);
let _ = writer.write_uvarint(4);
let _ = writer.write_uvarint(recipient_bytes.len() as u64);
let _ = writer.write_bytes(&recipient_bytes);
}
writer.into_bytes()
}
pub fn compute_transaction_hash(header_bytes: &[u8], body_bytes: &[u8]) -> [u8; 32] {
let header_hash = sha256_bytes(header_bytes);
let body_hash = sha256_bytes(body_bytes);
let mut combined = Vec::with_capacity(64);
combined.extend_from_slice(&header_hash);
combined.extend_from_slice(&body_hash);
sha256_bytes(&combined)
}
pub fn create_signing_preimage(
signature_metadata_hash: &[u8; 32],
transaction_hash: &[u8; 32],
) -> [u8; 32] {
let mut combined = Vec::with_capacity(64);
combined.extend_from_slice(signature_metadata_hash);
combined.extend_from_slice(transaction_hash);
sha256_bytes(&combined)
}
pub fn sha256_bytes(data: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(data);
let result = hasher.finalize();
let mut output = [0u8; 32];
output.copy_from_slice(&result);
output
}
pub fn compute_write_data_body_hash(entries_hex: &[String], scratch: bool, write_to_state: bool) -> [u8; 32] {
let body_without_entry = marshal_write_data_body_without_entry(scratch, write_to_state);
let body_part_hash = sha256_bytes(&body_without_entry);
let entry_hash = if entries_hex.is_empty() {
[0u8; 32]
} else {
compute_data_entry_hash(entries_hex)
};
merkle_hash(&[body_part_hash, entry_hash])
}
fn marshal_write_data_body_without_entry(scratch: bool, write_to_state: bool) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::WRITE_DATA);
if scratch {
let _ = writer.write_uvarint(3);
let _ = writer.write_uvarint(1);
}
if write_to_state {
let _ = writer.write_uvarint(4);
let _ = writer.write_uvarint(1);
}
writer.into_bytes()
}
pub fn compute_write_data_to_body_hash(recipient: &str, entries_hex: &[String]) -> [u8; 32] {
let body_without_entry = marshal_write_data_to_body_without_entry(recipient);
let body_part_hash = sha256_bytes(&body_without_entry);
let entry_hash = if entries_hex.is_empty() {
[0u8; 32]
} else {
compute_data_entry_hash(entries_hex)
};
merkle_hash(&[body_part_hash, entry_hash])
}
fn marshal_write_data_to_body_without_entry(recipient: &str) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::WRITE_DATA_TO);
let url_bytes = recipient.as_bytes();
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
writer.into_bytes()
}
fn compute_data_entry_hash(entries_hex: &[String]) -> [u8; 32] {
if entries_hex.is_empty() {
return [0u8; 32];
}
let mut data_hashes: Vec<[u8; 32]> = Vec::new();
for entry_hex in entries_hex {
if let Ok(data) = hex::decode(entry_hex) {
data_hashes.push(sha256_bytes(&data));
}
}
if data_hashes.is_empty() {
return [0u8; 32];
}
let merkle_root = merkle_hash(&data_hashes);
sha256_bytes(&merkle_root)
}
fn merkle_hash(hashes: &[[u8; 32]]) -> [u8; 32] {
if hashes.is_empty() {
return [0u8; 32];
}
if hashes.len() == 1 {
return hashes[0];
}
let mut pending: Vec<Option<[u8; 32]>> = Vec::new();
for hash in hashes {
let mut current = *hash;
let mut i = 0;
loop {
if i >= pending.len() {
pending.push(Some(current));
break;
}
if pending[i].is_none() {
pending[i] = Some(current);
break;
}
current = combine_hashes(&pending[i].unwrap(), ¤t);
pending[i] = None;
i += 1;
}
}
let mut anchor: Option<[u8; 32]> = None;
for v in &pending {
if anchor.is_none() {
anchor = *v;
} else if let Some(val) = v {
anchor = Some(combine_hashes(val, &anchor.unwrap()));
}
}
anchor.unwrap_or([0u8; 32])
}
fn combine_hashes(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
let mut combined = Vec::with_capacity(64);
combined.extend_from_slice(left);
combined.extend_from_slice(right);
sha256_bytes(&combined)
}
pub fn marshal_key_spec_params(key_hash: &[u8], delegate: Option<&str>) -> Vec<u8> {
let mut writer = BinaryWriter::new();
if !key_hash.is_empty() {
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(key_hash.len() as u64);
let _ = writer.write_bytes(key_hash);
}
if let Some(delegate_url) = delegate {
if !delegate_url.is_empty() {
let _ = writer.write_uvarint(2);
let delegate_bytes = delegate_url.as_bytes();
let _ = writer.write_uvarint(delegate_bytes.len() as u64);
let _ = writer.write_bytes(delegate_bytes);
}
}
writer.into_bytes()
}
pub fn marshal_key_page_operation(
op_type: &str,
key_hash: Option<&[u8]>,
delegate: Option<&str>,
old_key_hash: Option<&[u8]>,
new_key_hash: Option<&[u8]>,
threshold: Option<u64>,
) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let op_type_num = match op_type {
"add" => key_page_op_types::ADD,
"remove" => key_page_op_types::REMOVE,
"update" => key_page_op_types::UPDATE,
"setThreshold" => key_page_op_types::SET_THRESHOLD,
"setRejectThreshold" => key_page_op_types::SET_REJECT_THRESHOLD,
"setResponseThreshold" => key_page_op_types::SET_RESPONSE_THRESHOLD,
"updateAllowed" => key_page_op_types::UPDATE_ALLOWED,
_ => key_page_op_types::UNKNOWN,
};
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(op_type_num);
match op_type {
"add" | "remove" => {
if let Some(hash) = key_hash {
let entry_bytes = marshal_key_spec_params(hash, delegate);
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(entry_bytes.len() as u64);
let _ = writer.write_bytes(&entry_bytes);
}
}
"update" => {
if let Some(old_hash) = old_key_hash {
let old_entry_bytes = marshal_key_spec_params(old_hash, None);
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(old_entry_bytes.len() as u64);
let _ = writer.write_bytes(&old_entry_bytes);
}
if let Some(new_hash) = new_key_hash {
let new_entry_bytes = marshal_key_spec_params(new_hash, delegate);
let _ = writer.write_uvarint(3);
let _ = writer.write_uvarint(new_entry_bytes.len() as u64);
let _ = writer.write_bytes(&new_entry_bytes);
}
}
"setThreshold" | "setRejectThreshold" | "setResponseThreshold" => {
if let Some(thresh) = threshold {
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(thresh);
}
}
"updateAllowed" => {
}
_ => {}
}
writer.into_bytes()
}
pub fn marshal_update_key_page_body(operations: &[Vec<u8>]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::UPDATE_KEY_PAGE);
for op_bytes in operations {
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(op_bytes.len() as u64);
let _ = writer.write_bytes(op_bytes);
}
writer.into_bytes()
}
pub fn marshal_create_key_page_body(key_hashes: &[Vec<u8>]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::CREATE_KEY_PAGE);
for key_hash in key_hashes {
let key_spec_bytes = marshal_key_spec_params(key_hash, None);
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(key_spec_bytes.len() as u64);
let _ = writer.write_bytes(&key_spec_bytes);
}
writer.into_bytes()
}
pub fn marshal_burn_tokens_body(amount: u64) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::BURN_TOKENS);
if amount > 0 {
let _ = writer.write_uvarint(2);
let amount_bytes = amount_to_bigint_bytes(amount);
let _ = writer.write_uvarint(amount_bytes.len() as u64);
let _ = writer.write_bytes(&amount_bytes);
}
writer.into_bytes()
}
pub fn marshal_create_key_book_body(url: &str, public_key_hash: &[u8]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::CREATE_KEY_BOOK);
let url_bytes = url.as_bytes();
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
if !public_key_hash.is_empty() {
let _ = writer.write_uvarint(3);
let _ = writer.write_uvarint(public_key_hash.len() as u64);
let _ = writer.write_bytes(public_key_hash);
}
writer.into_bytes()
}
pub fn marshal_update_key_body(new_key_hash: &[u8]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::UPDATE_KEY);
if !new_key_hash.is_empty() {
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(new_key_hash.len() as u64);
let _ = writer.write_bytes(new_key_hash);
}
writer.into_bytes()
}
pub fn marshal_burn_credits_body(amount: u64) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::BURN_CREDITS);
if amount > 0 {
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(amount);
}
writer.into_bytes()
}
pub fn marshal_transfer_credits_body(recipients: &[(&str, u64)]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::TRANSFER_CREDITS);
for (url, amount) in recipients {
let recipient_bytes = marshal_credit_recipient(url, *amount);
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(recipient_bytes.len() as u64);
let _ = writer.write_bytes(&recipient_bytes);
}
writer.into_bytes()
}
fn marshal_credit_recipient(url: &str, amount: u64) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let url_bytes = url.as_bytes();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
if amount > 0 {
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(amount);
}
writer.into_bytes()
}
pub fn marshal_write_data_to_body(recipient: &str, entries_hex: &[String]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::WRITE_DATA_TO);
let url_bytes = recipient.as_bytes();
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
if !entries_hex.is_empty() {
let entry_bytes = marshal_data_entry(entries_hex);
let _ = writer.write_uvarint(3);
let _ = writer.write_uvarint(entry_bytes.len() as u64);
let _ = writer.write_bytes(&entry_bytes);
}
writer.into_bytes()
}
pub fn marshal_lock_account_body(height: u64) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::LOCK_ACCOUNT);
if height > 0 {
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(height);
}
writer.into_bytes()
}
pub mod account_auth_op_types {
pub const ENABLE: u64 = 1;
pub const DISABLE: u64 = 2;
pub const ADD_AUTHORITY: u64 = 3;
pub const REMOVE_AUTHORITY: u64 = 4;
}
pub fn marshal_update_account_auth_body(operations: &[(&str, &str)]) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(tx_types::UPDATE_ACCOUNT_AUTH);
for (op_type, authority_url) in operations {
let op_bytes = marshal_account_auth_operation(op_type, authority_url);
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(op_bytes.len() as u64);
let _ = writer.write_bytes(&op_bytes);
}
writer.into_bytes()
}
fn marshal_account_auth_operation(op_type: &str, authority_url: &str) -> Vec<u8> {
let mut writer = BinaryWriter::new();
let type_num = match op_type {
"enable" => account_auth_op_types::ENABLE,
"disable" => account_auth_op_types::DISABLE,
"add" | "addAuthority" => account_auth_op_types::ADD_AUTHORITY,
"remove" | "removeAuthority" => account_auth_op_types::REMOVE_AUTHORITY,
_ => 0,
};
let _ = writer.write_uvarint(1);
let _ = writer.write_uvarint(type_num);
if !authority_url.is_empty() {
let url_bytes = authority_url.as_bytes();
let _ = writer.write_uvarint(2);
let _ = writer.write_uvarint(url_bytes.len() as u64);
let _ = writer.write_bytes(url_bytes);
}
writer.into_bytes()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signature_metadata_hash() {
let public_key = [1u8; 32];
let signer = "acc://test.acme/book/1";
let signer_version = 1;
let timestamp = 1234567890000u64;
let hash = compute_ed25519_signature_metadata_hash(
&public_key,
signer,
signer_version,
timestamp,
);
assert_eq!(hash.len(), 32);
let hash2 = compute_ed25519_signature_metadata_hash(
&public_key,
signer,
signer_version,
timestamp,
);
assert_eq!(hash, hash2);
}
#[test]
fn test_transaction_hash() {
let header = b"test header";
let body = b"test body";
let hash = compute_transaction_hash(header, body);
assert_eq!(hash.len(), 32);
let hash2 = compute_transaction_hash(header, body);
assert_eq!(hash, hash2);
}
#[test]
fn test_signing_preimage() {
let sig_hash = [1u8; 32];
let tx_hash = [2u8; 32];
let preimage = create_signing_preimage(&sig_hash, &tx_hash);
assert_eq!(preimage.len(), 32);
}
#[test]
fn test_amount_encoding() {
assert_eq!(amount_to_bigint_bytes(0), vec![] as Vec<u8>);
assert_eq!(amount_to_bigint_bytes(1), vec![1]);
assert_eq!(amount_to_bigint_bytes(255), vec![255]);
assert_eq!(amount_to_bigint_bytes(256), vec![1, 0]);
assert_eq!(amount_to_bigint_bytes(0x123456), vec![0x12, 0x34, 0x56]);
}
}