diff --git a/apps/aecore/lib/aecore/channel/channel_state_on_chain.ex b/apps/aecore/lib/aecore/channel/channel_state_on_chain.ex index 2f7240c8..5556f84d 100644 --- a/apps/aecore/lib/aecore/channel/channel_state_on_chain.ex +++ b/apps/aecore/lib/aecore/channel/channel_state_on_chain.ex @@ -229,23 +229,9 @@ defmodule Aecore.Channel.ChannelStateOnChain do channel.channel_reserve })"} - # initiator/responder amounts can only be trusted when sequence == 0 - channel.sequence == 0 and poi_initiator_amount != channel.initiator_amount -> + poi_initiator_amount + poi_responder_amount > channel.total_amount -> {:error, - "#{__MODULE__}: Invalid initiator amount, expected #{channel.initiator_amount}, got #{ - poi_initiator_amount - }"} - - channel.sequence == 0 and poi_responder_amount != channel.responder_amount -> - {:error, - "#{__MODULE__}: Invalid responder amount, expected #{channel.responder_amount}, got #{ - poi_responder_amount - }"} - - # if sequence != 0 and no ChannelOffChainTx was provided then deposits or withdraws modified the total amount - poi_initiator_amount + poi_responder_amount != channel.total_amount -> - {:error, - "#{__MODULE__}: Invalid total amount, expected #{channel.total_amount}, got #{ + "#{__MODULE__}: Invalid total amount, expected at most #{channel.total_amount}, got #{ poi_initiator_amount + poi_responder_amount }"} @@ -289,9 +275,9 @@ defmodule Aecore.Channel.ChannelStateOnChain do channel.channel_reserve })"} - poi_initiator_amount + poi_responder_amount != channel.total_amount -> + poi_initiator_amount + poi_responder_amount > channel.total_amount -> {:error, - "#{__MODULE__}: Invalid total amount, expected #{channel.total_amount}, got #{ + "#{__MODULE__}: Invalid total amount, expected at most #{channel.total_amount}, got #{ poi_initiator_amount + poi_responder_amount }"} diff --git a/apps/aecore/lib/aecore/channel/channel_state_peer.ex b/apps/aecore/lib/aecore/channel/channel_state_peer.ex index c733ea91..0eadb787 100644 --- a/apps/aecore/lib/aecore/channel/channel_state_peer.ex +++ b/apps/aecore/lib/aecore/channel/channel_state_peer.ex @@ -208,8 +208,10 @@ defmodule Aecore.Channel.ChannelStatePeer do Enum.reduce_while(offchain_tx_list_from_oldest, {:ok, initial_state}, fn tx, {:ok, state} -> case process_fully_signed_tx(tx, state) do - {:ok, _} = new_acc -> - {:cont, new_acc} + {:ok, %ChannelStatePeer{mutually_signed_tx: prev_mutually_signed_tx} = new_state} -> + {:cont, + {:ok, + %ChannelStatePeer{new_state | mutually_signed_tx: [tx | prev_mutually_signed_tx]}}} {:error, _} = err -> {:halt, err} @@ -233,21 +235,19 @@ defmodule Aecore.Channel.ChannelStatePeer do mutually_signed_tx end - @doc """ - Calculates the state hash of the offchain chainstate after applying the transaction. - Only basic verification is done. - """ + # Calculates the state hash of the offchain chainstate after applying the transaction. + # Only basic verification is done. @spec calculate_next_state_hash_for_new_tx( ChannelTransaction.channel_tx(), ChannelStatePeer.t() ) :: {:ok, binary()} | error() - def calculate_next_state_hash_for_new_tx( - tx, - %ChannelStatePeer{ - channel_reserve: channel_reserve, - offchain_chainstate: offchain_chainstate - } = peer_state - ) do + defp calculate_next_state_hash_for_new_tx( + tx, + %ChannelStatePeer{ + channel_reserve: channel_reserve, + offchain_chainstate: offchain_chainstate + } = peer_state + ) do if ChannelTransaction.sequence(tx) <= ChannelStatePeer.sequence(peer_state) do {:error, "#{__MODULE__}: Too small sequence in tx. Received sequence(#{ @@ -279,6 +279,10 @@ defmodule Aecore.Channel.ChannelStatePeer do non_neg_integer(), Channel.role() ) :: ChannelStatePeer.t() + def initialize(_, _, _, _, _, :delegate) do + {:error, "#{__MODULE__}: Delegate can only load channel from open or txs list"} + end + def initialize( temporary_id, initiator_pubkey, @@ -545,11 +549,17 @@ defmodule Aecore.Channel.ChannelStatePeer do Keys.sign_priv_key(), map() ) :: {:ok, ChannelStatePeer.t(), ChannelTransaction.signed_tx()} | error() + def receive_half_signed_tx(_, _, _, _opts \\ %{}) + + def receive_half_signed_tx(%ChannelStatePeer{role: :delegate}, _, _, _) do + {:error, "#{__MODULE__}: Deleagte can't handle half_signed_txs"} + end + def receive_half_signed_tx( %ChannelStatePeer{fsm_state: :open} = peer_state, tx, privkey, - opts \\ %{} + opts ) do with :ok <- ChannelTransaction.half_signed_preprocess_check( @@ -578,6 +588,22 @@ defmodule Aecore.Channel.ChannelStatePeer do """ @spec receive_fully_signed_tx(ChannelStatePeer.t(), ChannelTransaction.signed_tx()) :: {:ok, ChannelStatePeer.t()} | error() + def receive_fully_signed_tx( + %ChannelStatePeer{ + fsm_state: :open, + role: :delegate + } = peer_state, + tx + ) do + case process_fully_signed_tx(tx, peer_state) do + {:ok, %ChannelStatePeer{mutually_signed_tx: prev_mutually_signed_tx} = new_state} -> + {:ok, %ChannelStatePeer{new_state | mutually_signed_tx: [tx | prev_mutually_signed_tx]}} + + {:error, _} = err -> + err + end + end + def receive_fully_signed_tx( %ChannelStatePeer{ fsm_state: :awaiting_full_tx, @@ -601,7 +627,8 @@ defmodule Aecore.Channel.ChannelStatePeer do end def receive_fully_signed_tx(%ChannelStatePeer{fsm_state: fsm_state}, _) do - {:error, "Unexpected 'receive_fully_signed_tx' call. Current state is: #{fsm_state}"} + {:error, + "#{__MODULE__}: Unexpected 'receive_fully_signed_tx' call. Current state is: #{fsm_state}"} end @doc """ @@ -644,6 +671,10 @@ defmodule Aecore.Channel.ChannelStatePeer do """ @spec transfer(ChannelStatePeer.t(), non_neg_integer(), Keys.sign_priv_key()) :: {:ok, ChannelStatePeer.t(), ChannelOffChainTx.t()} | error() + def transfer(%ChannelStatePeer{role: :delegate}, _, _) do + {:error, "#{__MODULE__}: Delegate can't transfer"} + end + def transfer( %ChannelStatePeer{fsm_state: :open, channel_id: channel_id} = peer_state, amount, @@ -685,6 +716,10 @@ defmodule Aecore.Channel.ChannelStatePeer do non_neg_integer(), Keys.sign_priv_key() ) :: {:ok, ChannelStatePeer.t(), SignedTx.t()} | error() + def withdraw(%ChannelStatePeer{role: :delegate}, _, _, _, _) do + {:error, "#{__MODULE__}: Delegate can't withdraw"} + end + def withdraw( %ChannelStatePeer{fsm_state: :open, channel_id: channel_id} = peer_state, amount, @@ -735,6 +770,10 @@ defmodule Aecore.Channel.ChannelStatePeer do non_neg_integer(), Keys.sign_priv_key() ) :: {:ok, ChannelStatePeer.t(), SignedTx.t()} | error() + def deposit(%ChannelStatePeer{role: :delegate}, _, _, _, _) do + {:error, "#{__MODULE__}: Delegate can't deposit"} + end + def deposit( %ChannelStatePeer{fsm_state: :open, channel_id: channel_id} = peer_state, amount, @@ -779,6 +818,10 @@ defmodule Aecore.Channel.ChannelStatePeer do Retrieves our offchain account balance from the latest offchain chainstate """ @spec our_offchain_account_balance(ChannelStatePeer.t()) :: non_neg_integer() + def our_offchain_account_balance(%ChannelStatePeer{role: :delegate}) do + throw({:error, "#{__MODULE__}: Delagate doesn't have a balance"}) + end + def our_offchain_account_balance( %ChannelStatePeer{offchain_chainstate: %Chainstate{accounts: accounts}} = peer_state ) do @@ -789,6 +832,10 @@ defmodule Aecore.Channel.ChannelStatePeer do Retrieves the foreign offchain account balance from the latest offchain chainstate """ @spec foreign_offchain_account_balance(ChannelStatePeer.t()) :: non_neg_integer() + def foreign_offchain_account_balance(%ChannelStatePeer{role: :delegate}) do + throw({:error, "#{__MODULE__}: There is no foreign peer for delegate"}) + end + def foreign_offchain_account_balance( %ChannelStatePeer{offchain_chainstate: %Chainstate{accounts: accounts}} = peer_state ) do @@ -865,6 +912,10 @@ defmodule Aecore.Channel.ChannelStatePeer do non_neg_integer(), Keys.sign_priv_key() ) :: {:ok, ChannelStatePeer.t(), SignedTx.t()} | error() + def close(%ChannelStatePeer{role: :delegate}, _, _, _) do + {:error, "#{__MODULE__}: Delegate can't close"} + end + def close( %ChannelStatePeer{ fsm_state: :open, @@ -927,6 +978,10 @@ defmodule Aecore.Channel.ChannelStatePeer do {non_neg_integer(), non_neg_integer()}, Keys.sign_priv_key() ) :: {:ok, ChannelStatePeer.t(), SignedTx.t()} | error() + def receive_close_tx(%ChannelStatePeer{role: :delegate}, _, _, _, _) do + {:error, "#{__MODULE__}: Deleagte can't receive close tx"} + end + def receive_close_tx( %ChannelStatePeer{ fsm_state: :open, @@ -1002,6 +1057,10 @@ defmodule Aecore.Channel.ChannelStatePeer do non_neg_integer(), Keys.sign_priv_key() ) :: {:ok, ChannelStatePeer.t(), SignedTx.t()} | error() + def solo_close(%ChannelStatePeer{role: :delegate}, _, _, _) do + {:error, "#{__MODULE__}: Delagate can't solo close"} + end + def solo_close( %ChannelStatePeer{ channel_id: channel_id, @@ -1041,13 +1100,19 @@ defmodule Aecore.Channel.ChannelStatePeer do @doc """ Creates a slash transaction from the most recent offchain chainstate. """ - @spec slash(ChannelStatePeer.t(), non_neg_integer(), non_neg_integer(), Keys.sign_priv_key()) :: - {:ok, ChannelStatePeer.t(), SignedTx.t()} + @spec slash( + ChannelStatePeer.t(), + non_neg_integer(), + non_neg_integer(), + Keys.pubkey(), + Keys.sign_priv_key() + ) :: {:ok, ChannelStatePeer.t(), SignedTx.t()} def slash( %ChannelStatePeer{channel_id: channel_id, mutually_signed_tx: [most_recent_tx | _]} = peer_state, fee, nonce, + pubkey, priv_key ) do new_peer_state = %ChannelStatePeer{peer_state | fsm_state: :closing} @@ -1060,7 +1125,7 @@ defmodule Aecore.Channel.ChannelStatePeer do poi: dispute_poi_for_latest_state(peer_state), offchain_tx: ChannelTransaction.dispute_payload(most_recent_tx) }, - our_pubkey(peer_state), + pubkey, fee, nonce ) @@ -1077,6 +1142,7 @@ defmodule Aecore.Channel.ChannelStatePeer do SignedTx.t(), non_neg_integer(), non_neg_integer(), + Keys.pubkey(), Keys.sign_priv_key() ) :: {:ok, ChannelStatePeer.t(), SignedTx.t() | nil} | error() def slashed( @@ -1090,6 +1156,7 @@ defmodule Aecore.Channel.ChannelStatePeer do }, fee, nonce, + pubkey, privkey ) do slash_hash = @@ -1105,7 +1172,7 @@ defmodule Aecore.Channel.ChannelStatePeer do # Because SlashTx/SoloTx was mined the state hash present here must have been verified onchain # If it was verified onchain then we needed to sign it - In conclusion we can rely on the state hash if slash_hash != ChannelTransaction.unsigned_payload(most_recent_tx).state_hash do - slash(peer_state, fee, nonce, privkey) + slash(peer_state, fee, nonce, pubkey, privkey) else new_peer_state = %ChannelStatePeer{peer_state | fsm_state: :closing} {:ok, new_peer_state, nil} @@ -1118,6 +1185,10 @@ defmodule Aecore.Channel.ChannelStatePeer do """ @spec snapshot(ChannelStatePeer.t(), non_neg_integer(), non_neg_integer(), Keys.sign_priv_key()) :: {:ok, SignedTx.t()} | error() + def snapshot(%ChannelStatePeer{role: :delegate}, _, _, _) do + {:error, "#{__MODULE__}: Delegate can't snapshot"} + end + def snapshot( %ChannelStatePeer{ fsm_state: state, @@ -1180,6 +1251,10 @@ defmodule Aecore.Channel.ChannelStatePeer do """ @spec settle(ChannelStatePeer.t(), non_neg_integer(), non_neg_integer(), Keys.sign_priv_key()) :: {:ok, SignedTx.t()} | error() + def settle(%ChannelStatePeer{role: :delegate}, _, _, _) do + {:error, "#{__MODULE__}: Deleagte can't settle"} + end + def settle( %ChannelStatePeer{ channel_id: channel_id, diff --git a/apps/aecore/lib/aecore/channel/tx/channel_close_solo_tx.ex b/apps/aecore/lib/aecore/channel/tx/channel_close_solo_tx.ex index 35b350f7..8618335c 100644 --- a/apps/aecore/lib/aecore/channel/tx/channel_close_solo_tx.ex +++ b/apps/aecore/lib/aecore/channel/tx/channel_close_solo_tx.ex @@ -173,7 +173,7 @@ defmodule Aecore.Channel.Tx.ChannelCloseSoloTx do !ChannelStateOnChain.active?(channel) -> {:error, "#{__MODULE__}: Can't solo close active channel. Use slash."} - sender != channel.initiator_pubkey && sender != channel.responder_pubkey -> + !ChannelStateOnChain.is_peer?(channel, sender) -> {:error, "#{__MODULE__}: Sender must be a party of the channel"} true -> diff --git a/apps/aecore/lib/aecore/channel/tx/channel_slash_tx.ex b/apps/aecore/lib/aecore/channel/tx/channel_slash_tx.ex index e0fe0f56..5a6faea4 100644 --- a/apps/aecore/lib/aecore/channel/tx/channel_slash_tx.ex +++ b/apps/aecore/lib/aecore/channel/tx/channel_slash_tx.ex @@ -165,6 +165,9 @@ defmodule Aecore.Channel.Tx.ChannelSlashTx do ChannelStateOnChain.active?(channel) -> {:error, "#{__MODULE__}: Can't slash active channel"} + !ChannelStateOnChain.is_peer_or_delegate?(channel, sender) -> + {:error, "#{__MODULE__}: Sender must be a party of the channel or a delagate"} + true -> ChannelStateOnChain.validate_slashing(channel, offchain_tx, poi) end diff --git a/apps/aecore/lib/aecore/channel/tx/channel_snapshot_solo_tx.ex b/apps/aecore/lib/aecore/channel/tx/channel_snapshot_solo_tx.ex index 908cbc5c..14880f63 100644 --- a/apps/aecore/lib/aecore/channel/tx/channel_snapshot_solo_tx.ex +++ b/apps/aecore/lib/aecore/channel/tx/channel_snapshot_solo_tx.ex @@ -155,11 +155,8 @@ defmodule Aecore.Channel.Tx.ChannelSnapshotSoloTx do !ChannelStateOnChain.active?(channel) -> {:error, "#{__MODULE__}: Can't submit snapshot when channel is closing (use slash)"} - !ChannelStateOnChain.is_peer_or_delegate?(channel, sender) -> - {:error, - "#{__MODULE__}: Sender #{sender} is not a peer or delegate of the channel, peers are: #{ - channel.initiator_pubkey - } and #{channel.responder_pubkey}, delegates are: #{channel.delegates} "} + !ChannelStateOnChain.is_peer?(channel, sender) -> + {:error, "#{__MODULE__}: Sender must be a party of the channel"} true -> ChannelStateOnChain.validate_snapshot(channel, offchain_tx) diff --git a/apps/aecore/lib/aecore/channel/worker.ex b/apps/aecore/lib/aecore/channel/worker.ex index 8180d6ec..0b4d03b2 100644 --- a/apps/aecore/lib/aecore/channel/worker.ex +++ b/apps/aecore/lib/aecore/channel/worker.ex @@ -24,7 +24,7 @@ defmodule Aecore.Channel.Worker do require Logger - @type role :: :initiator | :responder + @type role :: :initiator | :responder | :delegate @typedoc """ State is a map channel_id -> channel_peer_state @@ -312,11 +312,12 @@ defmodule Aecore.Channel.Worker do @doc """ Slashes channel. Creates slash Tx and adds it to the pool. """ - @spec slash(binary(), non_neg_integer(), non_neg_integer(), Keys.sign_priv_key()) :: + @spec slash(binary(), non_neg_integer(), non_neg_integer(), Keys.pubkey(), Keys.sign_priv_key()) :: :ok | error() - def slash(channel_id, fee, nonce, priv_key) - when is_binary(channel_id) and is_integer(fee) and is_integer(nonce) and is_binary(priv_key) do - GenServer.call(__MODULE__, {:slash, channel_id, fee, nonce, priv_key}) + def slash(channel_id, fee, nonce, pub_key, priv_key) + when is_binary(channel_id) and is_integer(fee) and is_integer(nonce) and is_binary(pub_key) and + is_binary(priv_key) do + GenServer.call(__MODULE__, {:slash, channel_id, fee, nonce, pub_key, priv_key}) end @doc """ @@ -326,11 +327,12 @@ defmodule Aecore.Channel.Worker do SignedTx.t(), non_neg_integer(), non_neg_integer(), + Keys.pubkey(), Keys.sign_priv_key() ) :: :ok | error() - def slashed(%SignedTx{} = slash_tx, fee, nonce, priv_key) - when is_integer(fee) and is_integer(nonce) and is_binary(priv_key) do - GenServer.call(__MODULE__, {:slashed, slash_tx, fee, nonce, priv_key}) + def slashed(%SignedTx{} = slash_tx, fee, nonce, pub_key, priv_key) + when is_integer(fee) and is_integer(nonce) and is_binary(pub_key) and is_binary(priv_key) do + GenServer.call(__MODULE__, {:slashed, slash_tx, fee, nonce, pub_key, priv_key}) end @doc """ @@ -629,10 +631,11 @@ defmodule Aecore.Channel.Worker do end end - def handle_call({:slash, channel_id, fee, nonce, priv_key}, _from, state) do + def handle_call({:slash, channel_id, fee, nonce, pub_key, priv_key}, _from, state) do peer_state = Map.get(state, channel_id) - with {:ok, new_peer_state, tx} <- ChannelStatePeer.slash(peer_state, fee, nonce, priv_key), + with {:ok, new_peer_state, tx} <- + ChannelStatePeer.slash(peer_state, fee, nonce, pub_key, priv_key), :ok <- Pool.add_transaction(tx) do {:reply, :ok, Map.put(state, channel_id, new_peer_state)} else @@ -654,6 +657,7 @@ defmodule Aecore.Channel.Worker do } = slash_tx, fee, nonce, + pub_key, priv_key }, _from, @@ -672,7 +676,7 @@ defmodule Aecore.Channel.Worker do peer_state = Map.get(state, channel_id) {:ok, new_peer_state, tx} = - ChannelStatePeer.slashed(peer_state, slash_tx, fee, nonce, priv_key) + ChannelStatePeer.slashed(peer_state, slash_tx, fee, nonce, pub_key, priv_key) if tx != nil do case Pool.add_transaction(tx) do diff --git a/apps/aecore/test/aecore_channels_test.exs b/apps/aecore/test/aecore_channels_test.exs index 3ebb09e8..53b479c3 100644 --- a/apps/aecore/test/aecore_channels_test.exs +++ b/apps/aecore/test/aecore_channels_test.exs @@ -26,6 +26,7 @@ defmodule AecoreChannelTest do @s1_name {:global, :Channels_S1} @s2_name {:global, :Channels_S2} + @s3_name {:global, :Channels_S3} setup do Code.require_file("test_utils.ex", "./test") @@ -60,8 +61,10 @@ defmodule AecoreChannelTest do GenServer.start_link(Channels, %{}, name: @s1_name) GenServer.start_link(Channels, %{}, name: @s2_name) + GenServer.start_link(Channels, %{}, name: @s3_name) assert %{} == call_s1(:get_all_channels) assert %{} == call_s2(:get_all_channels) + assert %{} == call_s3(:get_all_channels) %{ pk1: pk1, @@ -407,7 +410,7 @@ defmodule AecoreChannelTest do assert ChannelStateOnChain.active?(ChannelStateTree.get(Chain.chain_state().channels, id)) === false - assert :ok == call_s1({:slashed, solo_close_tx, 10, 2, ctx.sk1}) + assert :ok == call_s1({:slashed, solo_close_tx, 10, 2, ctx.pk1, ctx.sk1}) TestUtils.assert_transactions_mined() @@ -429,6 +432,54 @@ defmodule AecoreChannelTest do assert PatriciaMerkleTree.trie_size(Chain.chain_state().channels) == 0 end + @tag :channels + @tag timeout: 240_000 + test "Create channel, transfer twice, slash with old, delegate slashes with correct and settle", + ctx do + id = create_channel(ctx) + export_import_peer_state(id, &call_s1/1, &call_s3/1) + + tx = perform_transfer(id, 50, &call_s1/1, ctx.sk1, &call_s2/1, ctx.sk2) + assert :ok == call_s3({:receive_fully_signed_tx, tx}) + assert_offchain_state(id, 100, 200, 2) + + # prepare solo close but do not submit to pool + solo_close_tx = prepare_solo_close_tx(id, &call_s2/1, 15, 1, ctx.sk2) + + tx2 = perform_transfer(id, 170, &call_s2/1, ctx.sk2, &call_s1/1, ctx.sk1) + assert :ok == call_s3({:receive_fully_signed_tx, tx2}) + assert_offchain_state(id, 270, 30, 3) + + assert_custom_tx_succeeds(solo_close_tx) + + assert ChannelStateOnChain.active?(ChannelStateTree.get(Chain.chain_state().channels, id)) === + false + + assert :ok == call_s3({:slashed, solo_close_tx, 10, 1, ctx.pk3, ctx.sk3}) + [slash] = Map.values(Pool.get_pool()) + + TestUtils.assert_transactions_mined() + assert :ok == call_s1({:slashed, slash, 10, 1, ctx.pk1, ctx.sk1}) + + {:ok, s1_state} = call_s1({:get_channel, id}) + {:ok, settle_tx} = ChannelStatePeer.settle(s1_state, 10, 2, ctx.sk1) + assert :ok == Pool.add_transaction(settle_tx) + + Miner.mine_sync_block_to_chain() + assert Enum.empty?(Pool.get_pool()) == false + assert PatriciaMerkleTree.trie_size(Chain.chain_state().channels) == 1 + + TestUtils.assert_balance(ctx.pk1, 40) + TestUtils.assert_balance(ctx.pk2, 50 - 15) + TestUtils.assert_balance(ctx.pk3, 200 - 10) + + TestUtils.assert_transactions_mined() + + TestUtils.assert_balance(ctx.pk1, 40 - 10 + 270) + TestUtils.assert_balance(ctx.pk2, 50 - 15 + 30) + assert PatriciaMerkleTree.trie_size(Chain.chain_state().channels) == 0 + end + @tag :channels @tag timeout: 120_000 test "Create channel, responder disappears, solo close", ctx do @@ -480,7 +531,7 @@ defmodule AecoreChannelTest do snapshot_solo_tx = prepare_snapshot(id, &call_s2/1, 15, 2, ctx.sk2) # slashing an active channel fails - slash_tx = prepare_slash_tx(id, &call_s2/1, 15, 1, ctx.sk2) + slash_tx = prepare_slash_tx(id, &call_s2/1, 15, 1, ctx.pk2, ctx.sk2) assert_custom_tx_fails(slash_tx) assert ChannelStateOnChain.active?(ChannelStateTree.get(Chain.chain_state().channels, id)) === @@ -618,6 +669,9 @@ defmodule AecoreChannelTest do assert ChannelStatePeer.calculate_state_hash(responder_state) === ChannelStatePeer.calculate_state_hash(imported_responder_state) + + assert tx_list === ChannelStatePeer.get_signed_tx_list(imported_initiator_state) + assert tx_list === ChannelStatePeer.get_signed_tx_list(imported_responder_state) end @tag :channels @@ -630,7 +684,7 @@ defmodule AecoreChannelTest do perform_transfer(id, 50, &call_s1/1, ctx.sk1, &call_s2/1, ctx.sk2) [channel_create_tx] = ChannelStatePeer.get_signed_tx_list(initiator_state) solo_close_tx = prepare_solo_close_tx(id, &call_s2/1, 15, 1, ctx.sk2) - slash_tx = prepare_slash_tx(id, &call_s2/1, 15, 1, ctx.sk2) + slash_tx = prepare_slash_tx(id, &call_s2/1, 15, 1, ctx.pk2, ctx.sk2) snapshot_tx = prepare_snapshot(id, &call_s2/1, 15, 1, ctx.sk2) {:ok, settle_tx} = @@ -745,6 +799,8 @@ defmodule AecoreChannelTest do %ChannelOffChainTx{} = fully_signed_transfer_tx assert :open === get_fsm_state(id, responder_fun) :ok = initiator_fun.({:receive_fully_signed_tx, fully_signed_transfer_tx}) + + fully_signed_transfer_tx end defp perform_withdraw( @@ -887,9 +943,10 @@ defmodule AecoreChannelTest do assert responder_channel_balance == get_our_balance(id, responder_fun) end - defp prepare_slash_tx(id, peer_fun, fee, nonce, priv_key) when is_function(peer_fun, 1) do + defp prepare_slash_tx(id, peer_fun, fee, nonce, pub_key, priv_key) + when is_function(peer_fun, 1) do {:ok, state} = peer_fun.({:get_channel, id}) - {:ok, _, slash_tx} = ChannelStatePeer.slash(state, fee, nonce, priv_key) + {:ok, _, slash_tx} = ChannelStatePeer.slash(state, fee, nonce, pub_key, priv_key) slash_tx end @@ -905,6 +962,13 @@ defmodule AecoreChannelTest do slash_tx end + defp export_import_peer_state(id, from_server, to_server) do + {:ok, from_state} = from_server.({:get_channel, id}) + tx_list = ChannelStatePeer.get_signed_tx_list(from_state) + {:ok, to_state} = ChannelStatePeer.from_signed_tx_list(tx_list, :delegate) + to_server.({:import_channel, id, to_state}) + end + defp assert_custom_tx_fails(%SignedTx{} = tx) do assert :ok == Pool.add_transaction(tx) @@ -926,6 +990,10 @@ defmodule AecoreChannelTest do GenServer.call(@s2_name, call) end + defp call_s3(call) do + GenServer.call(@s3_name, call) + end + defp get_fsm_state_s1(id) do get_fsm_state(id, &call_s1/1) end