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

Utxo perf test #1756

Merged
merged 11 commits into from
Oct 29, 2020
26 changes: 26 additions & 0 deletions priv/perf/apps/load_test/lib/runner/utxos.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2019-2020 OmiseGO Pte Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

defmodule LoadTest.Runner.Utxos do
@moduledoc """
Utxos tests runner.
"""
use Chaperon.LoadTest

def scenarios do
[
{{1, LoadTest.Scenario.Utxos}, %{}}
]
end
end
2 changes: 1 addition & 1 deletion priv/perf/apps/load_test/lib/scenario/deposits.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ defmodule LoadTest.Scenario.Deposits do

session
|> Session.add_metric("error_rate", 1)
|> Session.add_error(:test, error)
|> Session.add_error(:error, error)
end
end

Expand Down
141 changes: 141 additions & 0 deletions priv/perf/apps/load_test/lib/scenario/utxos.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Copyright 2019-2020 OmiseGO Pte Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

defmodule LoadTest.Scenario.Utxos do
@moduledoc """
The scenario for utxos tests:

1. It creates two accounts: the sender and the receiver.
2. It funds sender with the specified amount on the childchain, checks utxos and balance.
3. The sender account sends the specifed amount on the childchain to the receiver,
checks its balance on the childchain and utxos for both accounts.

"""

use Chaperon.Scenario

alias Chaperon.Session
alias ExPlasma.Encoding
alias LoadTest.ChildChain.Transaction
alias LoadTest.Ethereum.Account
alias LoadTest.Service.Faucet
alias LoadTest.WatcherInfo.Balance
alias LoadTest.WatcherInfo.Utxo

@spec run(Session.t()) :: Session.t()
def run(session) do
tps = config(session, [:run_config, :tps])
period_in_seconds = config(session, [:run_config, :period_in_seconds])

total_number_of_transactions = tps * period_in_seconds
period_in_mseconds = period_in_seconds * 1_000

session
|> cc_spread(
:create_utxos_and_make_assertions,
total_number_of_transactions,
period_in_mseconds
)
|> await_all(:create_utxos_and_make_assertions)
end

def create_utxos_and_make_assertions(session) do
with {:ok, sender, receiver} <- create_accounts(),
{:ok, utxo} <- fund_account(session, sender),
:ok <- spend_utxo(session, utxo, sender, receiver) do
Session.add_metric(session, "error_rate", 0)
else
error ->
log_error(session, "#{__MODULE__} failed with #{inspect(error)}")

session
|> Session.add_metric("error_rate", 1)
|> Session.add_error(:error, error)
end
end

defp create_accounts() do
{:ok, sender_address} = Account.new()
{:ok, receiver_address} = Account.new()

{:ok, sender_address, receiver_address}
end

defp fund_account(session, account) do
initial_amount = config(session, [:chain_config, :initial_amount])
token = config(session, [:chain_config, :token])

with {:ok, utxo} <- fund_childchain_account(account, initial_amount, token),
:ok <-
fetch_childchain_balance(account,
amount: initial_amount,
token: Encoding.to_binary(token),
error: :wrong_childchain_after_funding
),
:ok <- validate_utxos(account, %{utxo | owner: account.addr}) do
{:ok, utxo}
end
end

defp validate_utxos(account, utxo) do
utxo_with_owner =
case utxo do
:empty -> :empty
_ -> %{utxo | owner: account.addr}
end

case Utxo.get_utxos(account, utxo_with_owner) do
{:ok, _} -> :ok
_other -> :invalid_utxos
end
end

defp fund_childchain_account(address, amount, token) do
case Faucet.fund_child_chain_account(address, amount, token) do
{:ok, utxo} -> {:ok, utxo}
_ -> :failed_to_fund_childchain_account
end
end

defp spend_utxo(session, utxo, sender, receiver) do
amount = config(session, [:chain_config, :initial_amount])
token = config(session, [:chain_config, :token])
fee = config(session, [:chain_config, :fee])
amount_to_transfer = amount - fee

with [new_utxo] <- Transaction.spend_utxo(utxo, amount_to_transfer, fee, sender, receiver, token),
:ok <- validate_utxos(sender, :empty),
:ok <- validate_utxos(receiver, %{new_utxo | owner: receiver.addr}),
:ok <-
fetch_childchain_balance(sender, amount: 0, token: Encoding.to_binary(token), error: :wrong_sender_balance),
:ok <-
fetch_childchain_balance(receiver,
amount: amount_to_transfer,
token: Encoding.to_binary(token),
error: :wrong_sender_balance
) do
:ok
end
end

defp fetch_childchain_balance(account, amount: amount, token: token, error: error) do
childchain_balance = Balance.fetch_balance(account.addr, amount, token)

case childchain_balance do
nil when amount == 0 -> :ok
%{"amount" => ^amount} -> :ok
Comment on lines +136 to +137
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you sure this two are the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nil when amount == 0 means we're expecting amount to be empty in the childchain for the specified currency. if it's not found in the json returned from watcherinfo, it means amount is zero.

_ -> error
end
end
end
8 changes: 7 additions & 1 deletion priv/perf/apps/load_test/lib/test_runner/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ defmodule LoadTest.TestRunner.Config do
alias ExPlasma.Encoding

@tests %{
"deposits" => LoadTest.Runner.Deposits
"deposits" => LoadTest.Runner.Deposits,
"utxos" => LoadTest.Runner.Utxos
}

@configs %{
Expand All @@ -29,6 +30,11 @@ defmodule LoadTest.TestRunner.Config do
deposited_amount: 200_000_000_000_000_000,
transferred_amount: 100_000_000_000_000_000,
gas_price: 2_000_000_000
},
"utxos" => %{
token: "0x0000000000000000000000000000000000000000",
initial_amount: 760,
fee: 75
}
}

Expand Down
2 changes: 1 addition & 1 deletion priv/perf/apps/load_test/lib/watcher_info/balance.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

defmodule LoadTest.WatcherInfo.Balance do
@moduledoc """
Functions related to balanes on the childchain
Functions related to balances on the childchain
"""
require Logger

Expand Down
100 changes: 100 additions & 0 deletions priv/perf/apps/load_test/lib/watcher_info/utxo.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Copyright 2019-2020 OmiseGO Pte Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.w

defmodule LoadTest.WatcherInfo.Utxo do
@moduledoc """
Functions for retrieving utxos through WatcherInfo API.
"""
require Logger

alias LoadTest.Connection.WatcherInfo
alias LoadTest.Ethereum.Account
alias LoadTest.Service.Sync
alias LoadTest.Utils.Encoding

@poll_timeout 60_000

@spec get_utxos(Account.addr_t(), ExPlasma.Utxo.t() | nil | :empty) :: {:ok, [] | ExPlasma.Utxo.t()} | no_return
def get_utxos(sender, utxo \\ nil) do
Sync.repeat_until_success(
fn ->
fetch_utxos(sender, utxo)
end,
@poll_timeout,
"Failed to fetch utxos"
)
end

defp fetch_utxos(sender, utxo) do
client = WatcherInfo.client()
body = %WatcherInfoAPI.Model.AddressBodySchema1{address: Encoding.to_hex(sender.addr)}
account_utxos = WatcherInfoAPI.Api.Account.account_get_utxos(client, body)

case account_utxos do
{:ok, result} ->
result.body
|> Jason.decode!()
|> find_utxo(utxo)

other ->
other
end
end

defp find_utxo(decoded_response, nil) do
{:ok, decoded_response}
end

defp find_utxo(%{"data" => []}, :empty) do
{:ok, []}
end

defp find_utxo(decoded_response, :empty) do
{:error, decoded_response}
end

defp find_utxo(decoded_response, utxo) do
do_find_utxo(decoded_response, utxo)
end

defp do_find_utxo(response, utxo) do
found_utxo =
Enum.find(response["data"], fn %{
"amount" => amount,
"blknum" => blknum,
"currency" => currency,
"oindex" => oindex,
"otype" => otype,
"owner" => owner,
"txindex" => txindex
} ->
current_utxo = %ExPlasma.Utxo{
amount: amount,
blknum: blknum,
currency: Encoding.to_binary(currency),
oindex: oindex,
output_type: otype,
owner: Encoding.to_binary(owner),
txindex: txindex
}

current_utxo == utxo
end)

case found_utxo do
nil -> {:error, response}
_ -> {:ok, found_utxo}
end
end
end
42 changes: 42 additions & 0 deletions priv/perf/apps/load_test/test/load_tests/runner/utxos_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2019-2020 OmiseGO Pte Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

defmodule LoadTest.Runner.UtxosTest do
use ExUnit.Case

@moduletag :utxos

test "deposits test" do
token = "0x0000000000000000000000000000000000000000"
initial_amount = 760
fee = 75

config = %{
chain_config: %{
token: token,
initial_amount: initial_amount,
fee: fee
},
run_config: %{
tps: 1,
period_in_seconds: 20
},
timeout: :infinity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why infinity??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the default timeout is 10_000 ms for task execution inside of chaperon

2020-10-29 13:24:13.437 [error] ⋅Task #PID<0.500.0> started from #PID<0.501.0> terminating
** (stop) exited in: Task.await(%Task{owner: #PID<0.501.0>, pid: #PID<0.879.0>, ref: #Reference<0.3543989965.2161639427.128884>}, 10000)
    ** (EXIT) time out
    (elixir 1.10.2) lib/task.ex:607: Task.await/2
    (chaperon 0.3.1) lib/chaperon/session.ex:366: Chaperon.Session.await/3
    (elixir 1.10.2) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
    (chaperon 0.3.1) lib/chaperon/scenario.ex:198: Chaperon.Scenario.with_scenario/3
    (chaperon 0.3.1) lib/chaperon/scenario.ex:172: Chaperon.Scenario.run/2
    (chaperon 0.3.1) lib/chaperon/scenario.ex:122: Chaperon.Scenario.execute/2
    (elixir 1.10.2) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
    (elixir 1.10.2) lib/task/supervised.ex:35: Task.Supervised.reply/5
    (stdlib 3.12) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

}

result = Chaperon.run_load_test(LoadTest.Runner.Utxos, config: config)

assert result.metrics["error_rate"][:mean] == 0.0
end
end
10 changes: 5 additions & 5 deletions priv/perf/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ config :ethereumex,
config :load_test,
pool_size: 5000,
max_connection: 5000,
retry_sleep: System.get_env("RETRY_SLEEP") || 1_000,
retry_sleep: "RETRY_SLEEP" |> System.get_env("1000") |> String.to_integer(),
child_chain_url: System.get_env("CHILD_CHAIN_URL") || "http://localhost:9656",
watcher_security_url: System.get_env("WATCHER_SECURITY_URL") || "http://localhost:7434",
watcher_info_url: System.get_env("WATCHER_INFO_URL") || "http://localhost:7534",
Expand All @@ -23,14 +23,14 @@ config :load_test,
"0xd885a307e35738f773d8c9c63c7a3f3977819274638d04aaf934a1e1158513ce",
eth_vault_address: System.get_env("CONTRACT_ADDRESS_ETH_VAULT"),
contract_address_payment_exit_game: System.get_env("CONTRACT_ADDRESS_PAYMENT_EXIT_GAME"),
child_block_interval: 1000,
child_block_interval: "CHILD_BLOCK_INTERVAL" |> System.get_env("1000") |> String.to_integer(),
contract_address_plasma_framework: System.get_env("CONTRACT_ADDRESS_PLASMA_FRAMEWORK"),
erc20_vault_address: System.get_env("CONTRACT_ADDRESS_ERC20_VAULT"),
test_currency: "0x0000000000000000000000000000000000000000",
faucet_deposit_amount: trunc(:math.pow(10, 14)),
fee_amount: 1,
deposit_finality_margin: 10,
gas_price: 2_000_000_000
fee_amount: "FEE_AMOUNT" |> System.get_env("75") |> String.to_integer(),
deposit_finality_margin: "DEPOSIT_FINALITY_MARGIN" |> System.get_env("10") |> String.to_integer(),
gas_price: "GAS_PRICE" |> System.get_env("2000000000") |> String.to_integer()

config :ex_plasma,
eip_712_domain: [
Expand Down