Skip to content
Snippets Groups Projects
Commit 67f40004 authored by x18zhan2's avatar x18zhan2
Browse files

worked headscache, some todos left

parent cf1e9102
No related branches found
No related tags found
No related merge requests found
Pipeline #1126 failed
# defmodule Server.Conf do
# def pubkey() do
# #TODO: read keypair from .yml file
# "BhQf2fkGAe1EszyZRJqasMr23YLSf1P3mhuYzEoKpgVA"
# end
# def seckey() do
# "2DLT4zDuuQSsEehijzkwCwdvH4vrsp6joBf4rum9Zb8jDTBKhDfZawSYsGeWy56cPDUGbWf5ybJgADNnVcnJ3sek"
# end
# def ws2p() do
# ""
# end
# end
defmodule Server.Conf.Keypair do
use GenServer
......
......@@ -8,72 +8,51 @@ defmodule WS2P.Cluster do
def init(_args) do
# initial heads_cache is an empty map
{:ok, Map.new()}
{:ok,
%{
"heads_cache" => %{},
"member_keys_cache" => %{},
"blockstamp_cache" => %{},
"new_heads_cache" => %{}
}}
end
def handle_call(:get_heads_cache, _from, state) do
def handle_call(:get_current_state, _from, state) do
{:reply, state, state}
end
def handle_call({:gen_new_head_for_new_block, block}, _from, heads_cache) do
# check block information, if it's all valid, if block is good, do following
pubkey = block["issuer"]
{:ok, seckey} = GenServer.call(:keypair, :get_seckey)
{:ok, ws2pid} = GenServer.call(:ws2pid, :get_ws2p_id)
blockstamp = block["number"] <> "-" <> block["hash"]
message =
"WS2POCA:HEAD:1:" <>
pubkey <> ":" <> blockstamp <> ":" <> ws2pid <> ":duniter:1.8.1:1"
messageV2 =
"WS2POCA:HEAD:1:" <>
pubkey <> ":" <> blockstamp <> ":" <> ws2pid <> ":duniter:1.8.1:1:1:1"
if Regex.compile!(Constants.head_v2_regexp()) |> Regex.match?(messageV2) and
Regex.compile!(Constants.head_v1_regexp()) |> Regex.match?(message) do
sigV2 = Crypto.digital_signature(messageV2, seckey)
sig = Crypto.digital_signature(message, seckey)
step = 0
my_full_id = pubkey <> "-" <> ws2pid
broadcast_message = %{
message: message,
sig: sig,
messageV2: messageV2,
sigV2: sigV2,
step: step
}
# update headcache
Map.put(heads_cache, my_full_id, broadcast_message)
GenServer.cast(WS2P.Connection, {:send_packet, broadcast_message})
{:ok, broadcast_message}
end
{:stop, :error, "Invalid block, can't generate a new head for it"}
end
def handle_call({:heads_received, heads}, _from, heads_cache) do
# if received_messages["body"]["name"] == "HEAD", do: WS2P.Cluster.heads_recived(received_messages["body"]["heads"])
def handle_call({:heads_received, heads}, _from, ws2p_cache) do
# for each head, call head_verification
IO.puts("I'm in heads_received")
new_valid_heads = Enum.map(heads, fn x -> formed_heads(x, heads_cache) end)
{:reply, new_valid_heads, heads_cache}
# if Enum.all?(new_valid_heads) do
# broadcast_message = Enum.into(new_valid_heads, %{})
# # Broadcast this message
# GenServer.cast(WS2P.Connection, {:send_packet, broadcast_message})
# else
# Logger.error("Stop broadcast these heads")
# end
# {:reply, to_be_broadcast_heads, to_be_broadcast_heads}
already_formed_heads = Enum.map(heads, fn x -> formed_heads(x) end)
updated_ws2p_cache = head_verification(already_formed_heads, ws2p_cache)
# veriify if all heads in heads_cache are valid, if they are, saved the new state and broadcast heads_cache
if map_size(updated_ws2p_cache) != 0 do
%{
"heads_cache" => _heads_cache,
"member_keys_cache" => _member_keys_cache,
"blockstamp_cache" => _blockstamp_cache,
"new_heads_cache" => new_heads_cache,
} = updated_ws2p_cache
if map_size(new_heads_cache) == length(heads) and not Enum.member?(new_heads_cache, nil) do
# put new_heads_cache's value to heads_cache and empty new_heads_cache
updated_ws2p_cache = Map.put(updated_ws2p_cache, "heads_cache", new_heads_cache)
updated_ws2p_cache = Map.put(updated_ws2p_cache, "new_heads_cache", %{})
# IO.inspect(updated_ws2p_cache)
{:reply, updated_ws2p_cache["heads_cache"], updated_ws2p_cache}
else
Logger.info("There is invalid head, shouldn't broadscast these heads")
end
else
Logger.error("There is invalid head, shouldn't broadscast these heads")
{:reply, ws2p_cache, ws2p_cache}
end
end
# This function returns %{pubkey <> "-" <> ws2pid, WS2PHeadCache.t()}
defp formed_heads(head, heads_cache) do
defp formed_heads(head) do
IO.puts("I'm in formed_received")
cond do
......@@ -93,7 +72,10 @@ defmodule WS2P.Cluster do
] = String.split(head["messageV2"], ":")
IO.puts("let's go to head verification, V2")
head_verification(head, pubkey, pubkey <> "-" <> ws2pid, blockstamp, heads_cache)
# IO.inspect([head, pubkey, pubkey <> "-" <> ws2pid, blockstamp])
[head, pubkey, pubkey <> "-" <> ws2pid, blockstamp]
# head_verification(head, pubkey, pubkey <> "-" <> ws2pid, blockstamp, ws2p_cache)
Regex.compile!(Constants.head_v1_regexp()) |> Regex.match?(head["message"]) ->
[
......@@ -110,7 +92,9 @@ defmodule WS2P.Cluster do
# Map.put(head, :blockstamp, blockstamp)
IO.puts("let's go to head verification")
head_verification(head, pubkey, pubkey <> "-" <> ws2pid, blockstamp, heads_cache)
[head, pubkey, pubkey <> "-" <> ws2pid, blockstamp]
# head_verification(head, pubkey, pubkey <> "-" <> ws2pid, blockstamp, ws2p_cache)
Regex.compile!(Constants.head_v0_regexp()) |> Regex.match?(head["message"]) ->
[
......@@ -121,41 +105,66 @@ defmodule WS2P.Cluster do
] = String.split(head["message"], ":")
# Map.put(head, :blockstamp, blockstamp)
head_verification(head, pubkey, pubkey <> "-" <> "000000", blockstamp, heads_cache)
[head, pubkey, pubkey <> "-" <> "000000", blockstamp]
# head_verification(head, pubkey, pubkey <> "-" <> "000000", blockstamp, ws2p_cache)
end
end
def head_verification(head, pubkey, full_id, blockstamp, heads_cache) do
def head_verification(
all_formed_heads,
ws2p_cache = %{
"heads_cache" => heads_cache,
"member_keys_cache" => member_keys_cache,
"blockstamp_cache" => blockstamp_cache,
"new_heads_cache" => new_heads_cache
}
) do
if length(all_formed_heads) == 0 do
IO.puts("Stop me")
# IO.inspect(ws2p_cache)
ws2p_cache
else
[current_head | current_tail] = all_formed_heads
[head, pubkey, full_id, blockstamp] = current_head
# if current head is not in headscache, check it and add it into cache
if (Regex.compile!(Constants.head_v0_regexp()) |> Regex.match?(head["message"]) or
Regex.compile!(Constants.head_v1_regexp()) |> Regex.match?(head["message"])) and
Regex.compile!(Constants.signature_regexp()) |> Regex.match?(head["sig"]) and
Regex.compile!(Constants.head_v2_regexp()) |> Regex.match?(head["messageV2"]) and
Regex.compile!(Constants.signature_regexp()) |> Regex.match?(head["sigV2"]) and
Regex.compile!(Constants.zero_or_positive_int()) |> Regex.match?(to_string(head["step"])) do
Regex.compile!(Constants.zero_or_positive_int())
|> Regex.match?(to_string(head["step"])) do
sig_ok = Crypto.verify_digital_signature(head["sig"], head["message"], pubkey)
sigV2_ok = Crypto.verify_digital_signature(head["sigV2"], head["messageV2"], pubkey)
IO.puts("passed regex verification")
if (sig_ok and sigV2_ok) or sig_ok do
IO.puts("signatures are valid")
step = if heads_cache[full_id]["step"] != nil, do: heads_cache[full_id]["step"], else: 0
if heads_cache[full_id] == nil or
Integer.parse(heads_cache[full_id]["blockstamp"]) < Integer.parse(blockstamp) or
(head["step"] != nil and head["step"] < step and
heads_cache[full_id]["blockstamp"] == blockstamp) do
# Check that issuer is a member and the block exists
server_pubkey = "BhQf2fkGAe1EszyZRJqasMr23YLSf1P3mhuYzEoKpgVA"
is_pubkey_existed = true
# Check if pubkey exists
# TODO: searched key in connected key
if server_pubkey || is_member_key(member_keys_cache, pubkey) do
# if current pubkey exists, put it into member keys cache
# TODO: figure out how to check is_pubkey_existed and is_block_existed
# Check that issuer is a member and the block exists
if is_pubkey_existed do
member_keys_cache = Map.put(member_keys_cache, pubkey, :os.system_time())
ws2p_cache = Map.put(ws2p_cache, "member_keys_cache", member_keys_cache)
IO.puts("public key exists")
is_block_existed = true
# if current block exists, put it into blockstamp_cache
blockstamp_cache = Map.put(blockstamp_cache, blockstamp, :os.system_time())
ws2p_cache = Map.put(ws2p_cache, "blockstamp_cache", blockstamp_cache)
if is_block_existed do
IO.puts("block exists")
new_head = %{
"blockstamp" => blockstamp,
......@@ -165,8 +174,12 @@ defmodule WS2P.Cluster do
"sigV2" => head["sigV2"],
"step" => step
}
heads_cache = Map.put(heads_cache, full_id, new_head)
heads_cache
# update new_heads_cache
new_heads_cache = Map.put(new_heads_cache, full_id, new_head)
# update ws2p
ws2p_cache = Map.put(ws2p_cache, "new_heads_cache", new_heads_cache)
head_verification(current_tail, ws2p_cache)
else
IO.puts("current head contains an unexisted block")
Logger.error("current head contains an unexisted block")
......@@ -193,6 +206,55 @@ defmodule WS2P.Cluster do
nil
end
end
end
defp is_member_key(member_keys_cache, pubkey) do
"""
this.isMemberKey(pub):
1. check memberkeysCache, memberkeysCache is a cache in this genserver. %{pubkey, date}
2. if not in memberkeysCache, searched db, each time after we searched db, we stored this pubkey in memberkeysCache
3. check memberkeysCache again and update its value as the current time
"""
is_member = false
if member_keys_cache[pubkey] do
is_member = true
end
# TODO: seach if member is in db, if it's in db, set is_member as true
# Do we have this block in the DB?
if not is_member do
# if member is in db
is_member = true
end
is_member
end
defp exists_block(blockstamp_cache, blockstamp) do
exists = false
if blockstamp_cache[blockstamp] do
exists = true
end
# TODO: searched the db to see if there is this block
"""
if (!exists) {
exists = !!(await this.server.dal.getAbsoluteBlockInForkWindowByBlockstamp(
blockstamp
));
}
"""
if not exists do
# Do we have this block in the DB?
exists = true
end
exists
end
@doc """
get a wss:// address or ws:// addresss
......@@ -212,28 +274,49 @@ defmodule WS2P.Cluster do
# Choose the web protocol depending on the port
protocol = if port == 433, do: "wss://", else: "ws://"
protocol <> host <> ":" <> port <> path
end
def handle_call({:gen_new_head_for_new_block, block}, _from, ws2p_cache) do
# check block information, if it's all valid, if block is good, do following
pubkey = block["issuer"]
# seckey = GenServer.call(:keypair, :get_seckey)
seckey = ""
ws2pid = ""
# ws2pid = GenServer.call(:ws2pid, :get_ws2p_id)
blockstamp = block["number"] <> "-" <> block["hash"]
message =
"WS2POCA:HEAD:1:" <>
pubkey <> ":" <> blockstamp <> ":" <> ws2pid <> ":duniter:1.8.1:1"
messageV2 =
"WS2POCA:HEAD:1:" <>
pubkey <> ":" <> blockstamp <> ":" <> ws2pid <> ":duniter:1.8.1:1:1:1"
if Regex.compile!(Constants.head_v2_regexp()) |> Regex.match?(messageV2) and
Regex.compile!(Constants.head_v1_regexp()) |> Regex.match?(message) do
sigV2 = Crypto.digital_signature(messageV2, seckey)
sig = Crypto.digital_signature(message, seckey)
step = 0
my_full_id = pubkey <> "-" <> ws2pid
broadcast_message = %{
message: message,
sig: sig,
messageV2: messageV2,
sigV2: sigV2,
step: step
}
# update headcache
[heads_cache, member_keys_cache, blockstamp_cache] = ws2p_cache
heads_cache = Map.put(heads_cache, my_full_id, broadcast_message)
ws2p_cache = Map.put(ws2p_cache, "heads_cache", heads_cache)
{:ok, broadcast_message, ws2p_cache}
end
# defmodule WS2PHead do
# @type t() :: %{
# message: String.t(),
# messageV2: String.t(),
# sig: String.t(),
# sigV2: String.t(),
# step: String.t()
# }
# end
# defmodule WS2PHeadCache do
# @type t() :: %{
# blockstamp: String.t(),
# message: String.t(),
# sig: String.t(),
# messageV2: String.t(),
# sigV2: String.t(),
# step: String.t()
# }
# end
{:stop, :error, "Invalid block, can't generate a new head for it"}
end
end
defmodule HeadsTest do
use ExUnit.Case
doctest Dunixir
# @head %{
# "message": "WS2POCAIC:HEAD:1:5B8iMAzq1dNmFe3ZxFTBQkqhq4fsztg1gZvxHXCk1XYH:690994-0000286014E9647880B606D480599BD451352901899BBE5DE295A06776189F8E:2bbc5904:duniter:1.8.1:1",
# "sig": "ZpUNqelhd7ckgf4idP/oFldkLzx6jbfKY5ZhvXL6A2x0Zw/iQ5Id1vg3bMqu7Fp6FnKrPB69ObB4k9TQ/WCDDA==",
# "messageV2": "WS2POCAIC:HEAD:2:5B8iMAzq1dNmFe3ZxFTBQkqhq4fsztg1gZvxHXCk1XYH:690994-0000286014E9647880B606D480599BD451352901899BBE5DE295A06776189F8E:2bbc5904:duniter:1.8.1:1:20:20",
# "sigV2": "ioHJ3XoxMMYSJcZZnrT6CqKAnBZim6dre/kc21LQL635cjecUSZmAicaaWygvnc2l6rO/Swt8gCYCLPnMs9zCw==",
# "step": 1
# } #Get this head from https://g1-test.duniter.org/network/ws2p/heads
# test "test head v1" do
# head = @head
# assert WS2P.CheckHeads.check_head_v1(%{message: head.message, sig: head.sig})
# end
# test "test head v2" do
# head = @head
# assert WS2P.CheckHeads.check_head_v2(%{messageV2: head.messageV2, sigV2: head.sigV2})
# end
end
......@@ -2,15 +2,29 @@ defmodule WS2P.Cluster.Test do
use ExUnit.Case
doctest Dunixir
@received_msg %{
"body" => %{
"heads" => [
%{
"message" => "WS2POCAIC:HEAD:1:HAy1hLpHfqrG3xsZRoBVkNigGQZnDfJK2az5MeRYtyNb:694027-0000988886FA69E41386B0E992B66DB4FE77BA4A5B29B5BBD04F85DFEDC80CF3:c4df202c:duniter:1.8.1:1",
"messageV2" => "WS2POCAIC:HEAD:2:HAy1hLpHfqrG3xsZRoBVkNigGQZnDfJK2az5MeRYtyNb:694027-0000988886FA69E41386B0E992B66DB4FE77BA4A5B29B5BBD04F85DFEDC80CF3:c4df202c:duniter:1.8.1:1:9:9",
"sig" => "FyUd1YZPFK483p9xJDveDeSPDQUYlr90FEgXDuxGajfc7t7pw7L2zEV/In0zTNX4O+hT8Otan2HTKw//JbkzCQ==",
"sigV2" => "GZgbXaVUKrFAg4iTYqh9EqmUg0yr/aZeeLEmuT2i9IFK9OBc+3LwdqbYdIdg9dcl3XwdbAFzkkV6qNqip2iCDg==",
"message" =>
"WS2POCAIC:HEAD:1:HAy1hLpHfqrG3xsZRoBVkNigGQZnDfJK2az5MeRYtyNb:694027-0000988886FA69E41386B0E992B66DB4FE77BA4A5B29B5BBD04F85DFEDC80CF3:c4df202c:duniter:1.8.1:1",
"messageV2" =>
"WS2POCAIC:HEAD:2:HAy1hLpHfqrG3xsZRoBVkNigGQZnDfJK2az5MeRYtyNb:694027-0000988886FA69E41386B0E992B66DB4FE77BA4A5B29B5BBD04F85DFEDC80CF3:c4df202c:duniter:1.8.1:1:9:9",
"sig" =>
"FyUd1YZPFK483p9xJDveDeSPDQUYlr90FEgXDuxGajfc7t7pw7L2zEV/In0zTNX4O+hT8Otan2HTKw//JbkzCQ==",
"sigV2" =>
"GZgbXaVUKrFAg4iTYqh9EqmUg0yr/aZeeLEmuT2i9IFK9OBc+3LwdqbYdIdg9dcl3XwdbAFzkkV6qNqip2iCDg==",
"step" => 2
},
%{
"message" =>
"WS2POCAIC:HEAD:1:3bGqhAELTU1R1br31Nmk9pt2oaAY3GL2ZwSWWjm7peeK:396970-0000000F49B173115846C603759633AD7E1B671C2A8AA50C4E9610290D0FFDD3:a87f1553:duniter:1.8.1:2",
"sig" =>
"8WzWMyJH1me5JTHfMK8mAsN+WA9mJNpCh2vEOxfegkgymk6yltrMDi4L5V8kbhhTMiAiNk8CqAs1CNuksba2CQ==",
"messageV2" =>
"WS2POCAIC:HEAD:2:3bGqhAELTU1R1br31Nmk9pt2oaAY3GL2ZwSWWjm7peeK:396970-0000000F49B173115846C603759633AD7E1B671C2A8AA50C4E9610290D0FFDD3:a87f1553:duniter:1.8.1:2:46:46",
"sigV2" =>
"gQ8WV8LnZxCt1vTgQT7+9c+7YRqUWYoBsTIpBP23oFkadUGIhNmDaKoZwSx0aG5p5b3w/PagZ3vENO0UGFfaBw==",
"step" => 2
}
],
......@@ -18,9 +32,9 @@ defmodule WS2P.Cluster.Test do
}
}
test "send heads to cluster" do
receivd_msg = @received_msg
if receivd_msg["body"]["name"] == "HEAD" do
to_be_sent_heads = receivd_msg["body"]["heads"]
# IO.inspect( to_be_sent_heads )
......@@ -28,22 +42,5 @@ defmodule WS2P.Cluster.Test do
new_valid_heads = GenServer.call(pid, {:heads_received, to_be_sent_heads})
IO.inspect(new_valid_heads)
end
end
end
"""
{:ok, pid} = GenServer.start_link(WS2P.Cluster, [])
heads = %{
"message" => "WS2POCAIC:HEAD:1:HAy1hLpHfqrG3xsZRoBVkNigGQZnDfJK2az5MeRYtyNb:694027-0000988886FA69E41386B0E992B66DB4FE77BA4A5B29B5BBD04F85DFEDC80CF3:c4df202c:duniter:1.8.1:1",
"messageV2" => "WS2POCAIC:HEAD:2:HAy1hLpHfqrG3xsZRoBVkNigGQZnDfJK2az5MeRYtyNb:694027-0000988886FA69E41386B0E992B66DB4FE77BA4A5B29B5BBD04F85DFEDC80CF3:c4df202c:duniter:1.8.1:1:9:9",
"sig" => "FyUd1YZPFK483p9xJDveDeSPDQUYlr90FEgXDuxGajfc7t7pw7L2zEV/In0zTNX4O+hT8Otan2HTKw//JbkzCQ==",
"sigV2" => "GZgbXaVUKrFAg4iTYqh9EqmUg0yr/aZeeLEmuT2i9IFK9OBc+3LwdqbYdIdg9dcl3XwdbAFzkkV6qNqip2iCDg==",
"step" => 2
}
GenServer.call(pid, heads)
"""
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment