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

165 transaction content max size #227

Merged
Merged
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
3 changes: 3 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ config :archethic, :mut_dir, "data"

config :archethic, :marker, "-=%=-=%=-=%=-"

# size represents in bytes binary
config :archethic, :transaction_data_content_max_size, 3_145_728

config :archethic, ArchEthic.Crypto,
supported_curves: [
:ed25519,
Expand Down
11 changes: 11 additions & 0 deletions lib/archethic/mining/pending_transaction_validation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ defmodule ArchEthic.Mining.PendingTransactionValidation do

with true <- Transaction.verify_previous_signature?(tx),
:ok <- validate_contract(tx),
:ok <- validate_content_size(tx),
:ok <- do_accept_transaction(tx) do
:telemetry.execute(
[:archethic, :mining, :pending_transaction_validation],
Expand All @@ -65,6 +66,16 @@ defmodule ArchEthic.Mining.PendingTransactionValidation do
end
end

defp validate_content_size(%Transaction{data: %TransactionData{content: content}}) do
content_max_size = Application.get_env(:archethic, :transaction_data_content_max_size)

if byte_size(content) >= content_max_size do
{:error, "Invalid node transaction with content size greaterthan content_max_size"}
else
:ok
end
end

defp validate_contract(%Transaction{data: %TransactionData{code: ""}}), do: :ok

defp validate_contract(%Transaction{
Expand Down
14 changes: 14 additions & 0 deletions lib/archethic_web/controllers/api/schema/transaction_data.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule ArchEthicWeb.API.Schema.TransactionData do
@moduledoc false
@content_max_size Application.compile_env!(:archethic, :transaction_data_content_max_size)

use Ecto.Schema
import Ecto.Changeset
Expand All @@ -22,5 +23,18 @@ defmodule ArchEthicWeb.API.Schema.TransactionData do
|> cast(params, [:code, :content, :recipients])
|> cast_embed(:ledger)
|> cast_embed(:ownerships)
|> validate_content_size()
end

defp validate_content_size(%Ecto.Changeset{} = changeset) do
validate_change(changeset, :content, fn field, content ->
content_size = byte_size(content)

if content_size >= @content_max_size do
[{field, "content size must be lessthan content_max_size"}]
else
[]
end
end)
end
end
10 changes: 10 additions & 0 deletions lib/archethic_web/controllers/api/transaction_payload.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ defmodule ArchEthicWeb.API.TransactionPayload do
:originSignature
])
|> cast_embed(:data, required: true)
|> validate_data()
end

def to_map(changes, acc \\ %{})
Expand All @@ -65,4 +66,13 @@ defmodule ArchEthicWeb.API.TransactionPayload do
end

def to_map(value, _), do: value

defp validate_data(%Ecto.Changeset{} = changeset) do
validate_change(changeset, :data, fn _, data_changeset ->
case data_changeset.valid? do
true -> []
false -> data_changeset.errors
end
end)
end
end
29 changes: 29 additions & 0 deletions test/archethic/mining/pending_transaction_validation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,35 @@ defmodule ArchEthic.Mining.PendingTransactionValidationTest do
PendingTransactionValidation.validate(tx)
end

test "should return an error when a node transaction content is greater than content_max_size " do
{public_key, private_key} = Crypto.derive_keypair("seed", 0)
{next_public_key, _} = Crypto.derive_keypair("seed", 1)
certificate = Crypto.get_key_certificate(public_key)

content_pretext =
<<80, 20, 10, 200, 3000::16, 1, 0, 4, 221, 19, 74, 75, 69, 16, 50, 149, 253, 24, 115, 128,
241, 110, 118, 139, 7, 48, 217, 58, 43, 145, 233, 77, 125, 190, 207, 31, 64, 157, 137>>

random_content = :crypto.strong_rand_bytes(4 * 1024 * 1024)

content =
content_pretext <> random_content <> <<byte_size(certificate)::16, certificate::binary>>

tx =
Transaction.new_with_keys(
:node,
%TransactionData{
content: content
},
private_key,
public_key,
next_public_key
)

assert {:error, "Invalid node transaction with content size greaterthan content_max_size"} =
PendingTransactionValidation.validate(tx)
end

test "should return :ok when a node shared secrets transaction data keys contains existing node public keys with first tx" do
P2P.add_and_connect_node(%Node{
ip: {127, 0, 0, 1},
Expand Down
22 changes: 22 additions & 0 deletions test/archethic_web/controllers/api/transaction_payload_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,28 @@ defmodule ArchEthicWeb.API.TransactionPayloadTest do
assert {"must be hexadecimal", _} = Keyword.get(errors, :content)
end

test "should return an error if the content size is greater than content max size" do
content = Base.encode16(:crypto.strong_rand_bytes(4 * 1024 * 1024))

%Ecto.Changeset{
valid?: false,
changes: %{data: %{errors: errors}}
} =
TransactionPayload.changeset(%{
"version" => 1,
"address" => Base.encode16(<<0::8, :crypto.strong_rand_bytes(32)::binary>>),
"type" => "transfer",
"timestamp" => DateTime.utc_now() |> DateTime.to_unix(:millisecond),
"previousPublicKey" =>
Base.encode16(<<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>),
"previousSignature" => Base.encode16(:crypto.strong_rand_bytes(64)),
"originSignature" => Base.encode16(:crypto.strong_rand_bytes(64)),
"data" => %{"content" => content}
})

assert {"content size must be lessthan content_max_size", _} = Keyword.get(errors, :content)
end

test "should return an error if the code is not a string" do
%Ecto.Changeset{
valid?: false,
Expand Down