Skip to content

Commit

Permalink
Verify p2p view when adding summary in aggregate
Browse files Browse the repository at this point in the history
  • Loading branch information
Neylix authored and samuelmanzanera committed Jul 10, 2023
1 parent 042aa3e commit 767a4c3
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 155 deletions.
70 changes: 52 additions & 18 deletions lib/archethic/beacon_chain/summary_aggregate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ defmodule Archethic.BeaconChain.SummaryAggregate do
)
|> Map.update!(:availability_adding_time, &[availability_adding_time | &1])

if bit_size(node_availabilities) > 0 or length(node_average_availabilities) > 0 or
length(end_of_node_synchronizations) > 0 do
if node_availabilities != <<>> or not Enum.empty?(node_average_availabilities) or
not Enum.empty?(end_of_node_synchronizations) or not Enum.empty?(network_patches) do
update_in(
agg,
[
Expand All @@ -85,22 +85,13 @@ defmodule Archethic.BeaconChain.SummaryAggregate do
})
],
fn prev ->
prev
|> Map.update!(
:node_availabilities,
&Enum.concat(&1, [Utils.bitstring_to_integer_list(node_availabilities)])
)
|> Map.update!(
:node_average_availabilities,
&Enum.concat(&1, [node_average_availabilities])
)
|> Map.update!(
:end_of_node_synchronizations,
&Enum.concat(&1, end_of_node_synchronizations)
)
|> Map.update!(
:network_patches,
&Enum.concat(&1, [network_patches])
add_p2p_availabilities(
subset,
prev,
node_availabilities,
node_average_availabilities,
end_of_node_synchronizations,
network_patches
)
end
)
Expand All @@ -109,6 +100,49 @@ defmodule Archethic.BeaconChain.SummaryAggregate do
end
end

defp add_p2p_availabilities(
subset,
map,
node_availabilities,
node_average_availabilities,
end_of_node_synchronizations,
network_patches
) do
map =
map
|> Map.update!(
:end_of_node_synchronizations,
&Enum.concat(&1, end_of_node_synchronizations)
)
|> Map.update!(
:network_patches,
&Enum.concat(&1, [network_patches])
)

expected_subset_length = P2PSampling.list_nodes_to_sample(subset) |> Enum.count()

map =
if bit_size(node_availabilities) == expected_subset_length do
map
|> Map.update!(
:node_availabilities,
&Enum.concat(&1, [Utils.bitstring_to_integer_list(node_availabilities)])
)
else
map
end

if Enum.count(node_average_availabilities) == expected_subset_length do
map
|> Map.update!(
:node_average_availabilities,
&Enum.concat(&1, [node_average_availabilities])
)
else
map
end
end

@doc """
Aggregate summaries batch
Expand Down
127 changes: 99 additions & 28 deletions test/archethic/beacon_chain/summary_aggregate_test.exs
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
defmodule Archethic.BeaconChain.SummaryAggregateTest do
use ArchethicCase

alias Archethic.BeaconChain.Summary
alias Archethic.BeaconChain.SummaryAggregate
alias Archethic.P2P
alias Archethic.P2P.Node
alias Archethic.BeaconChain.ReplicationAttestation
alias Archethic.TransactionChain.TransactionSummary

import Mock

doctest SummaryAggregate

setup do
P2P.add_and_connect_node(%Node{
first_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
last_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
ip: {127, 0, 0, 1},
port: 3000
})

P2P.add_and_connect_node(%Node{
first_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
last_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
ip: {127, 0, 0, 1},
port: 3001
})

:ok
end

describe "aggregate/1" do
test "should aggregate multiple network patches into a single one" do
P2P.add_and_connect_node(%Node{
first_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
last_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
ip: {127, 0, 0, 1},
port: 3000
})

P2P.add_and_connect_node(%Node{
first_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
last_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
ip: {127, 0, 0, 1},
port: 3001
})

assert %SummaryAggregate{
p2p_availabilities: %{
<<0>> => %{
Expand All @@ -49,20 +56,6 @@ defmodule Archethic.BeaconChain.SummaryAggregateTest do
end

test "should aggregate multiple different network patches into a single one" do
P2P.add_and_connect_node(%Node{
first_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
last_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
ip: {127, 0, 0, 1},
port: 3000
})

P2P.add_and_connect_node(%Node{
first_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
last_public_key: <<0::8, 0::8, 0::8, :crypto.strong_rand_bytes(31)::binary>>,
ip: {127, 0, 0, 1},
port: 3001
})

assert %SummaryAggregate{
p2p_availabilities: %{
<<0>> => %{
Expand Down Expand Up @@ -91,4 +84,82 @@ defmodule Archethic.BeaconChain.SummaryAggregateTest do
|> SummaryAggregate.aggregate()
end
end

describe "add_summary/2" do
setup_with_mocks [
{ReplicationAttestation, [], validate: fn _ -> :ok end}
] do
attestation = %ReplicationAttestation{
transaction_summary: %TransactionSummary{address: "addr1"}
}

attestation2 = %ReplicationAttestation{
transaction_summary: %TransactionSummary{address: "addr2"}
}

aggregate = %SummaryAggregate{
replication_attestations: [attestation],
p2p_availabilities: %{
<<0>> => %{
node_availabilities: [[1, 0]],
node_average_availabilities: [[0.95, 0.7]],
end_of_node_synchronizations: [],
network_patches: [["ABC", "DEF"]]
}
}
}

{:ok, %{aggregate: aggregate, attestation2: attestation2}}
end

test "should add summary into aggregate", %{
aggregate: aggregate = %SummaryAggregate{replication_attestations: previous_attestations},
attestation2: attestation2
} do
summary = %Summary{
subset: <<0>>,
node_availabilities: <<1::1, 1::1>>,
node_average_availabilities: [1, 0.8],
network_patches: ["DEF", "ABC"],
transaction_attestations: [attestation2]
}

assert %SummaryAggregate{
replication_attestations: [^attestation2 | ^previous_attestations],
p2p_availabilities: %{
<<0>> => %{
node_availabilities: [[1, 0], [1, 1]],
node_average_availabilities: [[0.95, 0.7], [1, 0.8]],
end_of_node_synchronizations: [],
network_patches: [["ABC", "DEF"], ["DEF", "ABC"]]
}
}
} = SummaryAggregate.add_summary(aggregate, summary)
end

test "should not add p2p view when summary one is invalid", %{
aggregate: aggregate = %SummaryAggregate{replication_attestations: previous_attestations},
attestation2: attestation2
} do
summary = %Summary{
subset: <<0>>,
node_availabilities: <<1::1>>,
node_average_availabilities: [0.8],
network_patches: ["DEF", "ABC"],
transaction_attestations: [attestation2]
}

assert %SummaryAggregate{
replication_attestations: [^attestation2 | ^previous_attestations],
p2p_availabilities: %{
<<0>> => %{
node_availabilities: [[1, 0]],
node_average_availabilities: [[0.95, 0.7]],
end_of_node_synchronizations: [],
network_patches: [["ABC", "DEF"], ["DEF", "ABC"]]
}
}
} = SummaryAggregate.add_summary(aggregate, summary)
end
end
end
Loading

0 comments on commit 767a4c3

Please sign in to comment.