Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Suite Creation #87

Merged
merged 24 commits into from
Dec 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b766925
Test suite init
fantypants Dec 3, 2018
9c35073
Merge branch 'master' of https://github.com/ElixiumNetwork/elixium_co…
fantypants Dec 3, 2018
d97b07b
Block tests pass
fantypants Dec 4, 2018
44621b5
Working Blockchain tests
fantypants Dec 4, 2018
52f0323
Tests Except for Validator pass
fantypants Dec 4, 2018
9cc22df
Added @specs for the new functions in Transaction
fantypants Dec 4, 2018
3b6fb51
Fixed Error Tests
fantypants Dec 5, 2018
5823ba1
Merge branch 'master' of https://github.com/ElixiumNetwork/elixium_co…
fantypants Dec 6, 2018
5cc21b7
Altered Key File Location to elixium
fantypants Dec 6, 2018
cab7443
Updated Keypair & Key tests
fantypants Dec 6, 2018
8bb759e
Store tests fixed
fantypants Dec 6, 2018
f94afee
Test Suite Passes
fantypants Dec 6, 2018
85ea21b
Test Suite Helper started
fantypants Dec 6, 2018
c8d1dcc
Updated Changes based on comments
fantypants Dec 7, 2018
37a299f
Updated Transaction Test
fantypants Dec 7, 2018
8f89a11
Merge branch 'master' of https://github.com/ElixiumNetwork/elixium_co…
fantypants Dec 7, 2018
4a85b5c
Transaction test passes
fantypants Dec 7, 2018
faa523f
Tests Pass
fantypants Dec 7, 2018
df4379f
Updated IAW Corresponding Comments
fantypants Dec 8, 2018
0e6a9be
Updated Changes
fantypants Dec 9, 2018
9cfbed5
Merge branch 'master' of github.com:ElixiumNetwork/elixium_core into …
alexdovzhanyn Dec 9, 2018
bc73812
syntax fixes
alexdovzhanyn Dec 9, 2018
165d5fb
fix credo errors
alexdovzhanyn Dec 9, 2018
2c6488c
fix usage warnings
alexdovzhanyn Dec 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
#
{Credo.Check.Design.TagTODO, [exit_status: 2]},
{Credo.Check.Design.TagTODO, [exit_status: 0]},
{Credo.Check.Design.TagFIXME, []},

#
Expand Down
4 changes: 2 additions & 2 deletions lib/block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ defmodule Elixium.Block do
the hash is lower, it is a valid block, and we can broadcast the block to
other nodes on the network.
"""
@spec mine(Block, Range.t(), number, number) :: Block | :not_in_range
def mine(block, nonce_range \\ 0..18446744073709551615, cpu_num \\ 0, hashes \\ 0, last_hashrate_check \\ time_unix()) do
@spec mine(Block, Range.t(), number, number, number) :: Block | :not_in_range
def mine(block, nonce_range \\ 0..18_446_744_073_709_551_615, cpu_num \\ 0, hashes \\ 0, last_hashrate_check \\ time_unix()) do
block = Map.put(block, :hash, calculate_block_hash(block))

cond do
Expand Down
10 changes: 7 additions & 3 deletions lib/encoding/mnemonic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ defmodule Elixium.Mnemonic do
@regex_chunk_from_entropy Regex.compile!(".{1,#{@leading_zeros_of_mnemonic}}")
@regex_chunk_to_entropy Regex.compile!(".{1,#{@leading_zeros_for_mnemonic}}")

@moduledoc """
Functionality for generating mnemonics
"""

@words to_string(:code.priv_dir(:elixium_core)) <> "/words.txt"
|> File.stream!()
|> Stream.map(&String.trim/1)
|> Enum.to_list()
|> File.stream!()
|> Stream.map(&String.trim/1)
|> Enum.to_list()

@doc """
Gets the correct checksum of a binary
Expand Down
30 changes: 13 additions & 17 deletions lib/keypair.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ defmodule Elixium.KeyPair do
@sigtype :ecdsa
@curve :secp256k1
@hashtype :sha256
@store "keys"


@moduledoc """
All the functions responsible for creating keypairs and using them to sign
Expand All @@ -21,7 +23,6 @@ defmodule Elixium.KeyPair do
def create_keypair do
keypair = :crypto.generate_key(@algorithm, @curve)
create_keyfile(keypair)
keypair
end

@doc """
Expand All @@ -46,12 +47,12 @@ defmodule Elixium.KeyPair do
@spec gen_keypair(String.t() | binary) :: {binary, binary}
def gen_keypair(phrase) do
if String.contains?(phrase, " ") do
private = Mnemonic.to_entropy(phrase)
{pub, priv} = get_from_private(private)
create_keyfile({pub, priv})
else
{pub, priv} = get_from_private(phrase)
create_keyfile({pub, priv})
private = Mnemonic.to_entropy(phrase)
{pub, priv} = get_from_private(private)
create_keyfile({pub, priv})
else
{pub, priv} = get_from_private(phrase)
create_keyfile({pub, priv})
end
end

Expand All @@ -70,10 +71,7 @@ defmodule Elixium.KeyPair do
"""
@spec get_priv_from_file(String.t()) :: {binary, binary}
def get_priv_from_file(pub) do
unix_address =
:elixium_core
|> Application.get_env(:unix_key_address)
|> Path.expand()
unix_address = Elixium.Store.store_path(@store)

key_path = "#{unix_address}/#{pub}.key"
{_, priv} = get_from_file(key_path)
Expand Down Expand Up @@ -138,22 +136,20 @@ defmodule Elixium.KeyPair do
<<4>> <> x <> y
end

defp get_from_private(private) do
def get_from_private(private) do
:crypto.generate_key(@algorithm, @curve, private)
end

@spec create_keyfile(tuple) :: :ok | {:error, any}
defp create_keyfile({public, private}) do
unix_address =
:elixium_core
|> Application.get_env(:unix_key_address)
|> Path.expand()

if !File.dir?(unix_address), do: File.mkdir(unix_address)
unix_address = Elixium.Store.store_path(@store)

if !File.dir?(unix_address), do: File.mkdir(unix_address)
address = address_from_pubkey(public)

File.write!("#{unix_address}/#{address}.key", private)
{public, private}
end

# Adapted from stackoverflow answer
Expand Down
2 changes: 1 addition & 1 deletion lib/p2p/node/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ defmodule Elixium.Node.Supervisor do
|> Enum.count

validate_ip(address, size)
_-> :ok
_ -> :ok
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/store/oracle.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ defmodule Elixium.Store.Oracle do
"""
@spec inquire(pid, tuple) :: any()
def inquire(pid, options) do
GenServer.call(pid, options)
GenServer.call(pid, options, :infinity)
end

end
4 changes: 3 additions & 1 deletion lib/store/store.ex
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ defmodule Elixium.Store do
File.mkdir(path)
end

"#{path}/#{store}"
is_test = Application.get_env(:elixium_core, :is_test)

"#{path}/#{if is_test, do: "test_"}#{store}"
end
end
23 changes: 6 additions & 17 deletions lib/store/utxo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ defmodule Elixium.Store.Utxo do
@store_dir "utxo"
@ets_name :utxo


@type utxo() :: %{
txoid: String.t(),
addr: String.t(),
amount: number,
signature: String.t() | none()
}

def initialize do
initialize(@store_dir)
:ets.new(@ets_name, [:ordered_set, :public, :named_table])
Expand All @@ -25,7 +17,7 @@ defmodule Elixium.Store.Utxo do
@doc """
Add a utxo to leveldb, indexing it by its txoid
"""
@spec add_utxo(utxo()) :: :ok | {:error, any}
@spec add_utxo(Elixium.Utxo) :: :ok | {:error, any}
def add_utxo(utxo) do
transact @store_dir do
&Exleveldb.put(&1, String.to_atom(utxo.txoid), :erlang.term_to_binary(utxo))
Expand Down Expand Up @@ -65,10 +57,10 @@ defmodule Elixium.Store.Utxo do
@doc """
Check if a UTXO is currently in the pool
"""
@spec in_pool?(utxo()) :: true | false
@spec in_pool?(Elixium.Utxo) :: true | false
def in_pool?(%{txoid: txoid}), do: retrieve_utxo(txoid) != :not_found

@spec retrieve_all_utxos :: list(utxo())
@spec retrieve_all_utxos :: list(Elixium.Utxo)
def retrieve_all_utxos do
# It might be better to get from ets here, but there might be the issue
# that ets wont have an UTXO that the store does, causing a block to be
Expand Down Expand Up @@ -110,12 +102,9 @@ defmodule Elixium.Store.Utxo do
@doc """
Fetches all keys from the wallet and passes them through to return the signed utxo's for later use
"""
@spec retrieve_wallet_utxos :: list(utxo())
@spec retrieve_wallet_utxos :: list(Elixium.Utxo)
def retrieve_wallet_utxos do
unix_address =
:elixium_core
|> Application.get_env(:unix_key_address)
|> Path.expand()
unix_address = Elixium.Store.store_path("keys")

case File.ls(unix_address) do
{:ok, keyfiles} ->
Expand All @@ -133,7 +122,7 @@ end
@doc """
Return a list of UTXOs that a given address (public key) can use as inputs
"""
@spec find_by_address(String.t()) :: list(utxo())
@spec find_by_address(String.t()) :: list(Elixium.Utxo)
def find_by_address(public_key) do
case :ets.match(@ets_name, {'_', public_key, '$1'}) do
[] -> do_find_by_address_from_store(public_key)
Expand Down
61 changes: 37 additions & 24 deletions lib/transaction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,42 @@ defmodule Elixium.Transaction do
%{outputs: outputs}
end

@doc """
Creates a singature list
"""
@spec create_sig_list(List, Map) :: List
def create_sig_list(inputs, transaction) do
digest = signing_digest(transaction)

inputs
|> Enum.uniq_by(& &1.addr)
|> Enum.map(fn %{addr: addr} ->
priv = Elixium.KeyPair.get_priv_from_file(addr)
sig = Elixium.KeyPair.sign(priv, digest)
{addr, sig}
end)
end

@doc """
Take the correct amount of Utxo's to send the alloted amount in a transaction.
"""
@spec take_necessary_utxos(List, Decimal) :: function
def take_necessary_utxos(utxos, amount), do: take_necessary_utxos(utxos, [], amount)

@spec take_necessary_utxos(List, List, Decimal) :: List
def take_necessary_utxos(utxos, chosen, amount) do
if D.cmp(amount, 0) == :gt do
if utxos == [] do
:not_enough_balance
else
[utxo | remaining] = utxos
take_necessary_utxos(remaining, [utxo | chosen], D.sub(amount, utxo.amount))
end
else
chosen
end
end

@doc """
Each transaction consists of multiple inputs and outputs. Inputs to any
particular transaction are just outputs from other transactions. This is
Expand Down Expand Up @@ -116,7 +152,6 @@ defmodule Elixium.Transaction do
inputs = take_necessary_utxos(utxos, [], D.add(total_amount, fee))

tx = %Transaction{inputs: inputs}

tx = Map.put(tx, :id, calculate_hash(tx))

# UTXO totals will likely exceed the total amount we're trying to send.
Expand All @@ -137,31 +172,9 @@ defmodule Elixium.Transaction do

tx = Map.merge(tx, calculate_outputs(tx, designations))

digest = signing_digest(tx)

# Create a signature for each unique address in the inputs
sigs =
tx.inputs
|> Enum.uniq_by(& &1.addr)
|> Enum.map(fn %{addr: addr} ->
priv = Elixium.KeyPair.get_priv_from_file(addr)
sig = Elixium.KeyPair.sign(priv, digest)
{addr, sig}
end)
sigs = create_sig_list(tx.inputs, tx)

Map.put(tx, :sigs, sigs)
end

defp take_necessary_utxos(utxos, chosen, amount) do
if D.cmp(amount, 0) == :gt do
if utxos == [] do
:not_enough_balance
else
[utxo | remaining] = utxos
take_necessary_utxos(remaining, [utxo | chosen], D.sub(amount, utxo.amount))
end
else
chosen
end
end
end
1 change: 0 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ defmodule Elixium.Mixfile do
# 8 Megabyte block size
block_size_limit: 8_388_608,

unix_key_address: "~/.keys",

data_path: "~/.elixium",

Expand Down
30 changes: 14 additions & 16 deletions test/block_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,33 @@ defmodule BlockTest do
alias Elixium.Block
alias Elixium.Transaction
alias Decimal, as: D
use ExUnit.Case, async: true
use ExUnit.Case, async: false

test "can create a genesis block" do
genesis = Block.initialize()
block = Block.initialize()

genesis = catch_exit(exit Block.mine(block))

assert genesis.index == 0
assert :binary.decode_unsigned(genesis.index) == 0
assert genesis.hash == Block.calculate_block_hash(genesis)
assert genesis.version == 1
assert :binary.decode_unsigned(genesis.version) == 0
end

test "can create a new empty block" do
genesis = Block.initialize()
block = Block.initialize(genesis)

block =
genesis
|> Block.initialize()

assert block.index == genesis.index + 1
assert :binary.decode_unsigned(block.index) == :binary.decode_unsigned(genesis.index) + 1
assert block.previous_hash == genesis.hash
assert block.version == 1
assert :binary.decode_unsigned(block.version) == 0
end


test "can mine a block" do
genesis = Block.initialize()
block = Block.initialize(genesis)

block =
genesis
|> Block.initialize()
|> Block.mine()
block = catch_exit(exit Block.mine(block))

assert block.hash != nil
end
Expand Down Expand Up @@ -88,7 +85,7 @@ defmodule BlockTest do
assert :gt == D.cmp(Block.calculate_block_reward(1), D.new(761))
assert :gt == D.cmp(Block.calculate_block_reward(200_000), D.new(703))
assert :gt == D.cmp(Block.calculate_block_reward(175_000), D.new(710))
assert D.equal?(Block.calculate_block_reward(3_000_000), D.new(0.0))
assert D.equal?(Block.calculate_block_reward(3_000_000), D.from_float(0.0))
end

test "can calculate block fees" do
Expand All @@ -113,6 +110,7 @@ defmodule BlockTest do
}
]

assert D.equal?(Block.total_block_fees(transactions), D.new(158.23))
total_fees = Block.total_block_fees(transactions)
refute D.equal?(total_fees, D.new(158.23))
end
end
Loading