defmodule Elixium.BlockEncoder do alias Elixium.Block @moduledoc """ Provides functionality for encoding and decoding blocks """ @encoding_order [ :index, :hash, :previous_hash, :merkle_root, :timestamp, :nonce, :difficulty, :version, :transactions ] @doc """ Encode a block to a binary representation based on the encoding order: @encoding_order """ @spec encode(Block) :: binary def encode(block) do block = Map.delete(block, :__struct__) Enum.reduce(@encoding_order, <<>>, fn attr, bin -> encode(attr, bin, block[attr]) end) end defp encode(:difficulty, bin, value) do # Convert to binary and strip out ETF bytes (we dont need them for storage, # we can add them back in when we need to read) <<131, 70, difficulty::binary>> = :erlang.term_to_binary(value) bin <> difficulty end defp encode(:transactions, bin, value) do # Add transactions in as raw ETF encoding for easy decoding later bin <> :erlang.term_to_binary(value) end defp encode(_attr, bin, value) when is_binary(value) do bin <> value end defp encode(_attr, bin, value) when is_number(value) do bin <> :binary.encode_unsigned(value) end @doc """ Decode a block from binary that was previously encoded by encode/1 """ @spec decode(binary) :: Block def decode(block_binary) do <> = block_binary %Block{ index: index, hash: hash, previous_hash: previous_hash, merkle_root: merkle_root, timestamp: :binary.decode_unsigned(timestamp), nonce: nonce, difficulty: :erlang.binary_to_term(<<131, 70>> <> difficulty), version: version, transactions: :erlang.binary_to_term(transactions) } end end