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

[GH-757] Add delegates to Channel.Worker #809

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
22 changes: 4 additions & 18 deletions apps/aecore/lib/aecore/channel/channel_state_on_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
}"}

Expand Down Expand Up @@ -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
}"}

Expand Down
113 changes: 94 additions & 19 deletions apps/aecore/lib/aecore/channel/channel_state_peer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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(#{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"}
Copy link
Contributor

Choose a reason for hiding this comment

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

Deleagte -> Delegate

end

def receive_half_signed_tx(
%ChannelStatePeer{fsm_state: :open} = peer_state,
tx,
privkey,
opts \\ %{}
opts
Copy link
Contributor

Choose a reason for hiding this comment

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

By default no additional options should be passed for update validation.

) do
with :ok <-
ChannelTransaction.half_signed_preprocess_check(
Expand Down Expand Up @@ -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,
Expand All @@ -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 """
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"})
Copy link
Contributor

Choose a reason for hiding this comment

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

Are the throws handled anywhere and can they be replaced with regular {:error, _} returns?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should only throw due to programmer error. It doesn't make sense to take a balance of delegate. It makes even less sens to get foreign_offchain_account_balance when ChannelStatePeer is for delegate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We use this functions only in places where it's know the ChannelStatePeer.role is not delegate.

end

def our_offchain_account_balance(
%ChannelStatePeer{offchain_chainstate: %Chainstate{accounts: accounts}} = peer_state
) do
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"}
Copy link
Contributor

Choose a reason for hiding this comment

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

Deleagte -> Delegate

end

def receive_close_tx(
%ChannelStatePeer{
fsm_state: :open,
Expand Down Expand Up @@ -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"}
Copy link
Contributor

Choose a reason for hiding this comment

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

Delagate -> Delegate

end

def solo_close(
%ChannelStatePeer{
channel_id: channel_id,
Expand Down Expand Up @@ -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}
Expand All @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

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

If the role is :initiator or :responder we should check that pubkey == our_pubkey(peer_state)

fee,
nonce
)
Expand All @@ -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(
Expand All @@ -1090,6 +1156,7 @@ defmodule Aecore.Channel.ChannelStatePeer do
},
fee,
nonce,
pubkey,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above

privkey
) do
slash_hash =
Expand All @@ -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}
Expand All @@ -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,
Expand Down Expand Up @@ -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"}
Copy link
Contributor

Choose a reason for hiding this comment

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

Deleagte -> Delegate

end

def settle(
%ChannelStatePeer{
channel_id: channel_id,
Expand Down
2 changes: 1 addition & 1 deletion apps/aecore/lib/aecore/channel/tx/channel_close_solo_tx.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand Down
3 changes: 3 additions & 0 deletions apps/aecore/lib/aecore/channel/tx/channel_slash_tx.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 2 additions & 5 deletions apps/aecore/lib/aecore/channel/tx/channel_snapshot_solo_tx.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading