Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use Mix.Config

config :daisy,
serializer: Daisy.Serializer.JSONSerializer,
serializer: Daisy.Serializer,
run_leader: false,
run_follower: false,
run_api: false
run_api: false,
initial_block_reference: :genesis

import_config "#{Mix.env}.exs"
5 changes: 3 additions & 2 deletions lib/daisy/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Daisy.Application do
runner = Daisy.Config.get_runner()
reader = Daisy.Config.get_reader()
ipfs_key = Daisy.Config.get_ipfs_key()
initial_block_reference = Daisy.Config.initial_block_reference()

# TODO: Check that IPFS is up and available, when?

Expand Down Expand Up @@ -34,12 +35,12 @@ defmodule Daisy.Application do
children = children ++ cond do
Daisy.Config.run_leader?() ->
[
Supervisor.Spec.worker(Daisy.Tracker, [Daisy.Storage, :resolve, runner, reader, [name: Daisy.Tracker]]),
Supervisor.Spec.worker(Daisy.Tracker, [Daisy.Storage, initial_block_reference, :leader, runner, reader, [name: Daisy.Tracker]]),
Supervisor.Spec.worker(Daisy.Tracker.Leader, [Daisy.Tracker, [name: Daisy.Tracker.Leader]])
]
Daisy.Config.run_follower?() ->
[
Supervisor.Spec.worker(Daisy.Tracker, [Daisy.Storage, :resolve, nil, reader, [name: Daisy.Tracker]]),
Supervisor.Spec.worker(Daisy.Tracker, [Daisy.Storage, initial_block_reference, :follower, runner, reader, [name: Daisy.Tracker]]),
Supervisor.Spec.worker(Daisy.Tracker.Follower, [Daisy.Tracker, Daisy.Storage, [name: Daisy.Tracker.Follower]])
]
true ->
Expand Down
12 changes: 10 additions & 2 deletions lib/daisy/block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ defmodule Daisy.Block do
"""
@spec final_storage(block_hash, identifier()) :: {:ok, Daisy.Storage.root_hash} | {:error, any()}
def final_storage(block_hash, storage_pid) do
case read_data_from_block_hash(block_hash, storage_pid, "block/final_storage") do
case read_link_from_block_hash(block_hash, storage_pid, "final_storage_link") do
{:ok, final_storage} -> {:ok, final_storage}
:not_found -> Daisy.Storage.new(storage_pid)
els -> els
Expand All @@ -48,7 +48,7 @@ defmodule Daisy.Block do
"""
@spec block_number(block_hash, identifier()) :: {:ok, Daisy.Storage.root_hash} | {:error, any()}
def block_number(block_hash, storage_pid) do
with {:ok, block_number} <- read_data_from_block_hash(block_hash, storage_pid, "block/number") do
with {:ok, block_number} <- read_data_from_block_hash(block_hash, storage_pid, "block_number") do
{:ok, block_number |> String.to_integer}
end
end
Expand All @@ -62,6 +62,14 @@ defmodule Daisy.Block do
end
end

defp read_link_from_block_hash(block_hash, storage_pid, path) do
case Daisy.Storage.get_hash(storage_pid, block_hash, path) do
{:ok, value} -> {:ok, value}
:not_found -> {:error, "cannot find #{path} in stored block `#{block_hash}`"}
els -> els
end
end

@doc """
Executes a function to read its value (without affecting any state).

Expand Down
7 changes: 4 additions & 3 deletions lib/daisy/block/builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ defmodule Daisy.Block.Builder do
iex> {:ok, storage_pid} = Daisy.Storage.start_link()
iex> Daisy.Block.Builder.genesis_block(storage_pid)
{:ok, %Daisy.Data.Block{
block_number: 0,
parent_block_hash: "",
block_number: 1,
parent_block_hash: "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n",
initial_storage: "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n",
final_storage: "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n",
transactions: [],
Expand All @@ -25,7 +25,8 @@ defmodule Daisy.Block.Builder do
def genesis_block(storage_pid) do
with {:ok, initial_storage} <- Daisy.Storage.new(storage_pid) do
{:ok, Daisy.Data.Block.new(
block_number: 0,
block_number: 1,
parent_block_hash: initial_storage,
initial_storage: initial_storage,
final_storage: initial_storage,
transactions: []
Expand Down
2 changes: 2 additions & 0 deletions lib/daisy/block/chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ defmodule Daisy.Block.Chain do
"""
@spec compare_blocks(Daisy.Data.Block.t, Daisy.Data.Block.t) :: :match | {:mismatch, [{atom(), any(), any()}]}
def compare_blocks(block_a, block_b) do
IO.inspect(["Comparing", block_a, block_b])

mismatches = Enum.reduce(Map.from_struct(block_a), [], fn {k, v1}, mismatches ->
if (v2 = Map.get(block_b, k)) == v1 do
mismatches
Expand Down
5 changes: 5 additions & 0 deletions lib/daisy/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ defmodule Daisy.Config do
@default_port 2335
@default_scheme :http

@spec initial_block_reference :: any()
def initial_block_reference do
Application.get_env(:daisy, :initial_block_reference, :resolve)
end

@spec run_api? :: boolean()
def run_api? do
Application.get_env(:daisy, :run_api, false)
Expand Down
53 changes: 28 additions & 25 deletions lib/daisy/examples/kitten.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ defmodule Daisy.Examples.Kitten do
Kitten is an example Daisy library. This emulates a simple crypto-kitty
like world.
"""
alias Daisy.Random

defmodule Data do
defmodule Kitten do
defstruct [:uuid, :name, :owner]
defstruct [:id, :name, :owner]

@type t :: %__MODULE__{
uuid: String.t,
id: String.t,
name: String.t,
owner: String.t | nil
}
Expand All @@ -19,7 +20,7 @@ defmodule Daisy.Examples.Kitten do
kitten_data = Poison.decode!(kitten_json)

%__MODULE__{
uuid: kitten_data["uuid"],
id: kitten_data["id"],
name: kitten_data["name"],
owner: kitten_data["owner"] |> Daisy.Encoder.maybe_decode_58!,
}
Expand All @@ -28,7 +29,7 @@ defmodule Daisy.Examples.Kitten do
@spec serialize(t) :: String.t
def serialize(kitten) do
%{
"uuid" => kitten.uuid,
"id" => kitten.id,
"name" => kitten.name,
"owner" => kitten.owner |> Daisy.Encoder.maybe_encode_58,
} |> Poison.encode!
Expand All @@ -54,15 +55,15 @@ defmodule Daisy.Examples.Kitten do
end

@spec add_new(t, String.t) :: t
def add_new(orphan, uuid) do
def add_new(orphan, id) do
orphan
|> Kernel.++([uuid])
|> Kernel.++([id])
end

@spec remove(t, String.t) :: t
def remove(orphan, uuid) do
def remove(orphan, id) do
orphan
|> Enum.reject(&(&1 == uuid))
|> Enum.reject(&(&1 == id))
end
end
end
Expand All @@ -79,14 +80,14 @@ defmodule Daisy.Examples.Kitten do
end
end

def read("is_orphan?", [kitten_uuid], storage_pid, storage) do
def read("is_orphan?", [kitten_id], storage_pid, storage) do
with {:ok, orphans} <- read("orphans", [], storage_pid, storage) do
{:ok, Enum.member?(orphans, kitten_uuid)}
{:ok, Enum.member?(orphans, kitten_id)}
end
end

def read("kitten", [kitten_uuid], storage_pid, storage) do
with {:ok, kitten_json} <- Daisy.Storage.get(storage_pid, storage, "/kittens/#{kitten_uuid}") do
def read("kitten", [kitten_id], storage_pid, storage) do
with {:ok, kitten_json} <- Daisy.Storage.get(storage_pid, storage, "/kittens/#{kitten_id}") do
{:ok, Data.Kitten.deserialize(kitten_json)}
end
end
Expand All @@ -100,28 +101,30 @@ defmodule Daisy.Examples.Kitten do

@callback run_transaction(Daisy.Data.Invokation.t, identifier(), Daisy.Storage.root_hash, integer(), binary()) :: {:ok, Daisy.Runner.transaction_result} | {:error, any()}
def run_transaction(%Daisy.Data.Invokation{function: "spawn", args: [cooldown]}, storage_pid, initial_storage, block_number, owner) do
# Generate a new random identifier and name
first_name = @first_names |> Enum.random
title = @titles |> Enum.random
# Generate a new pseudo-random identifier and name
rand = Random.init(block_number)
{rand, first_name} = Random.random_el(rand, @first_names)
{rand, title} = Random.random_el(rand, @titles)
{_rand, kitten_id} = Random.unique_id(rand)

kitten = %Data.Kitten{
uuid: UUID.uuid4(),
id: kitten_id,
name: "#{first_name} #{title}"
}

# Add kitten to storage
{:ok, storage_with_kitten} = Daisy.Storage.put_new(
storage_pid,
initial_storage,
"/kittens/#{kitten.uuid}",
"/kittens/#{kitten.id}",
kitten |> Data.Kitten.serialize)

# Add new kitten to list of orphans
# TODO: default is... default, not default running function, possibly change behaviour?
{:ok, storage_with_kitten_as_orphan} = Daisy.Storage.update(storage_pid, storage_with_kitten, "/orphans", fn orphan_json ->
orphan_json
|> Data.Orphan.deserialize
|> Data.Orphan.add_new(kitten.uuid)
|> Data.Orphan.add_new(kitten.id)
|> Data.Orphan.serialize
end, default: "", run_update_fn_on_default: true)

Expand All @@ -140,32 +143,32 @@ defmodule Daisy.Examples.Kitten do
{:ok, %{
final_storage: storage_with_queued_transaction,
logs: [
"Added new kitten #{kitten.uuid} from owner #{inspect owner}"
"Added new kitten #{kitten.id} from owner #{inspect owner}"
],
debug: inspect kitten
}}
end

def run_transaction(%Daisy.Data.Invokation{function: "adopt", args: [kitten_uuid]}, storage_pid, storage_1, _block_number, owner) do
def run_transaction(%Daisy.Data.Invokation{function: "adopt", args: [kitten_id]}, storage_pid, storage_1, _block_number, owner) do
# First, check to see that the kitten is up for adoption
case Reader.read("is_orphan?", [kitten_uuid], storage_pid, storage_1) do
case Reader.read("is_orphan?", [kitten_id], storage_pid, storage_1) do
{:ok, false} ->
{:ok, %{
status: :failure,
logs: ["Kitten #{kitten_uuid} not up for adoption"]
logs: ["Kitten #{kitten_id} not up for adoption"]
}}
{:error, error} -> {:error, error}
{:ok, true} ->
# Remove kitten from orphans
{:ok, storage_2} = Daisy.Storage.update(storage_pid, storage_1, "/orphans", fn orphan_json ->
orphan_json
|> Data.Orphan.deserialize
|> Data.Orphan.remove(kitten_uuid)
|> Data.Orphan.remove(kitten_id)
|> Data.Orphan.serialize
end)

# Add kitten as adopted
{:ok, storage_3} = Daisy.Storage.update(storage_pid, storage_2, "/kittens/#{kitten_uuid}", fn kitten_json ->
{:ok, storage_3} = Daisy.Storage.update(storage_pid, storage_2, "/kittens/#{kitten_id}", fn kitten_json ->
kitten_json
|> Data.Kitten.deserialize
|> Map.put(:owner, owner)
Expand All @@ -175,7 +178,7 @@ defmodule Daisy.Examples.Kitten do
{:ok, %{
final_storage: storage_3,
logs: [
"Owner #{inspect owner} adopted kitten #{kitten_uuid}"
"Owner #{inspect owner} adopted kitten #{kitten_id}"
]
}}
end
Expand Down
Loading