Skip to content

Instantly share code, notes, and snippets.

@yatender-oktalk
Created September 26, 2020 23:13
Show Gist options
  • Save yatender-oktalk/b58edebf8b69ab9151711643818da4cd to your computer and use it in GitHub Desktop.
Save yatender-oktalk/b58edebf8b69ab9151711643818da4cd to your computer and use it in GitHub Desktop.
defmodule ExChain.Blockchain.BlockTest do
@moduledoc """
This module contains test related to a block
"""
use ExUnit.Case
alias ExChain.Blockchain.Block
describe "block" do
test "genesis is valid" do
assert %Block{
data: "genesis data",
hash: "F277BF9150CD035D55BA5B48CB5BCBE8E564B134E5AD0D56E439DD04A1528D3B",
last_hash: "-",
timestamp: 1_599_909_623_805_627
} == Block.genesis()
end
test "mine block returns new block" do
%Block{hash: hash} = genesis_block = Block.genesis()
assert %Block{
data: "this is mined data",
last_hash: ^hash
} = Block.mine_block(genesis_block, "this is mined data")
end
test "new give a new block when we pass the parameters" do
# setup the data
timestamp = DateTime.utc_now() |> DateTime.to_unix(1_000_000)
last_hash = "random_hash"
data = "this is new block data"
# Perform assertions
assert %Block{timestamp: ^timestamp, hash: _hash, last_hash: ^last_hash, data: ^data} =
Block.new(timestamp, last_hash, data)
end
end
end
defmodule ExChain.Blockchain.Block do
@moduledoc """
This module is the single block struct in a blockchain
"""
alias __MODULE__
@type t :: %Block{
timestamp: pos_integer(),
last_hash: String.t(),
hash: String.t(),
data: any()
}
defstruct ~w(timestamp last_hash hash data)a
@spec new(pos_integer(), String.t(), any()) :: Block.t()
def new(timestamp, last_hash, data) do
%__MODULE__{}
|> add_timestamp(timestamp)
|> add_last_hash(last_hash)
|> add_data(data)
|> add_hash()
end
@spec get_str(Block.t()) :: String.t()
def get_str(block = %__MODULE__{}) do
"""
Block
timestamp: #{block.timestamp}
last_hash: #{block.last_hash}
hash: #{block.hash}
data: #{block.data}
"""
end
@spec genesis() :: Block.t()
def genesis() do
__MODULE__.new(1_599_909_623_805_627, "-", "genesis data")
end
def mine_block(%__MODULE__{hash: last_hash}, data) do
__MODULE__.new(get_timestamp(), last_hash, data)
end
# private functions
defp add_timestamp(%__MODULE__{} = block, timestamp), do: %{block | timestamp: timestamp}
defp add_data(%__MODULE__{} = block, data), do: %{block | data: data}
defp add_last_hash(%__MODULE__{} = block, last_hash), do: %{block | last_hash: last_hash}
defp add_hash(%__MODULE__{timestamp: timestamp, last_hash: last_hash, data: data} = block) do
%{block | hash: hash(timestamp, last_hash, data)}
end
defp get_timestamp(), do: DateTime.utc_now() |> DateTime.to_unix(1_000_000)
defp hash(timestamp, last_hash, data) do
data = "#{timestamp}:#{last_hash}:#{Jason.encode!(data)}"
Base.encode16(:crypto.hash(:sha256, data))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment