Understanding Code base of state channel - `udpate channel` part

I tried understanding code of update channel flow. Would be like some one to verify it. TIA

Code Snippet for channel update at FSM 1 Side

Expose channel.update.new rpc first

process_request(#{<<"method">> := <<"channels.update.new">> = M,
                   <<"params">> := #{<<"from">>    := FromB,
                                     <<"to">>      := ToB,
                                     <<"amount">>  := Amount} = Params}, FsmPid) ->
    assert_integer(Amount),
    case {aeser_api_encoder:safe_decode(account_pubkey, FromB),
          aeser_api_encoder:safe_decode(account_pubkey, ToB)} of
        {{ok, From}, {ok, To}} ->
            apply_with_optional_params(
              M, Params,
              fun(XParams) ->
                      aesc_fsm:upd_transfer(FsmPid, From, To, Amount, XParams)
              end);
        _ -> {error, {broken_encoding, [account]}}
    end;

Handle the RPC call in handle_upd_transfer method

Currently in open state

handle_upd_transfer(FromPub, ToPub, Amount, From, UOpts, #data{ state = State
                                                              , opts = Opts
                                                              , on_chain_id = ChannelId
                                                              } = D) ->
     %% Get the current block_hash
    {BlockHash, OnChainEnv, OnChainTrees} = pick_onchain_env(UOpts, D),
    ActiveProtocol = aetx_env:consensus_version(OnChainEnv),
    try
        %%  Form the update object with from, to and amount params
        Updates = [aesc_offchain_update:op_transfer(aeser_id:create(account, FromPub),
                                                    aeser_id:create(account, ToPub), Amount)
                   | meta_updates(UOpts)],
        lager:debug("TODO_IM: handle_upd_transfer: Updates = ~p", [Updates]),
        %%  Prepare the transaction to be signed
        Tx1 = aesc_offchain_state:make_update_tx(Updates, State, ChannelId, ActiveProtocol,
                                                 OnChainTrees, OnChainEnv,
                                                 channel_reserve(Opts)),
       %%  Request to sdk for signing 
        case request_signing(?UPDATE, Tx1, Updates, BlockHash, D, defer) of
            {ok, Send, D1, Actions} ->
                %% reply before sending sig request
                gen_statem:reply(From, ok),
                Send(),
               %%  Move to awaiting_signature state
                next_state(awaiting_signature, D1, Actions);
            {error, _} = Error ->
                gen_statem:reply(From, Error),
                keep_state(D)
        end
   ...
   ...

Set the half signed state before sending this halfSgined Tx to FSM 1

check_signed_update_ack_tx(SignedTx, Msg,
                           #data{ state = State
                                , opts = Opts
                                , op = #op_ack{tag = ?UPDATE} = Op} = D) ->
...
...
{ok, D#data{ state = aesc_offchain_state:set_signed_tx(
                                    SignedTx, Updates, State, OnChainTrees, OnChainEnv, Opts)
                        , log = log_msg(rcv, ?UPDATE_ACK, Msg, D#data.log)}};
...
...

Now send the halfSignedTx to FSM 2

send_update_msg(SignedTx, Updates,
                #data{ on_chain_id = OnChainId
                     , session = Sn
                     , op = #op_ack{ tag  = ?UPDATE
                                   , data = OpData}} = Data) ->
    ...
    ...
    aesc_session_noise:update(Sn, Msg);
    ...
    ...

And you go back to the awaiting_update_ask state after checking the signature


awaiting_signature(cast, {?SIGNED, ?UPDATE = OpTag, SignedTx} = Msg,
                   #data{ state = State
                        , op = #op_sign{ tag = OpTag
                                       , data = OpData0 } = OpSign} = D) ->
          %% Check the signature
          maybe_check_auth(SignedTx, OpSign, not_offchain_tx, me, 
          ...
          ...
              %% Go to awaiting_update_ack state
              next_state(awaiting_update_ack, D3)
          ...
          ...

Code Snippet for channel update at FSM 2 Side

Receive the transaction, check for message authenticity and request the sdk to sign the message

open(cast, {?UPDATE, Msg}, D) ->
    %% Check the message first which came from FSM1
    case check_update_msg(Msg, D) of
        {ok, SignedTx, Updates, BlockHash, D1} ->
            D2 = report(info, update, D1),
            %% Request SDK2 to sign 
            case request_signing_(?UPDATE_ACK, SignedTx, Updates, BlockHash, D2) of
                {ok, D3, Actions} ->
                    %% Let's wait for SDK2 sign back. Moved to `awaiting_signature` state
                    next_state(awaiting_signature, set_ongoing(?UPDATE, D3), Actions); %% next_state(st, D, opts)
                {error, _Error} ->
                    handle_update_conflict(?UPDATE, D2)
            end;
...
...

After it gets the signed message (full signed), he moves stores the state and goes into open state

awaiting_signature(cast, {?SIGNED, ?UPDATE_ACK = OpTag, SignedTx} = Msg,
                   #data{ op = #op_sign{ tag = OpTag
                                       , data = OpData0 } = OpSign
                        , opts = Opts
                        , state = State } = D) ->
    ...
    ...
    %% Check the signautres 
    maybe_check_auth(SignedTx, OpSign, not_offchain_tx, both,
        fun() ->
            D1 = log(rcv, ?SIGNED, Msg, D),
            %% Respond to FSM1 about this fullSigned message
            D2 = send_update_ack_msg(SignedTx, D1),
            {OnChainEnv, OnChainTrees} = load_pinned_env(BlockHash),
            %% Stores the full signed state
            State1 = aesc_offchain_state:set_signed_tx(SignedTx, Updates, State,
                                                       OnChainTrees, OnChainEnv, Opts),
            D3 = D2#data{ state = State1
                        , op    = ?NO_OP },
            %% Goes back to open state
            next_state(open, D3)
        end, D);

Sends the fullSignedtx to FSM 1

send_update_ack_msg(SignedTx, #data{ on_chain_id = OnChainId
                                   , session     = Sn } = Data) ->
    %% serialize before sending via Noise to FSM1
    TxBin = aetx_sign:serialize_to_binary(SignedTx),
    Msg = #{ channel_id => OnChainId
           , data       => #{tx => TxBin} },
    %% Send it now!   
    aesc_session_noise:update_ack(Sn, Msg),

Code Snippet for channel update at FSM 1 Side

Currently in awaiting_update_ack state

Receives the message, check the validity, responds to SDK 1, stores the state, goes back to open state

awaiting_update_ack(cast, {?UPDATE_ACK, Msg}, #data{} = D) ->
    %% Check for the incomming message  
    case check_update_ack_msg(Msg, D) of
    {ok, D1} ->
            next_state(open, D1);
...
...

Deserialize and check the validity

check_update_ack_msg_(#{ channel_id := ChanId
                       , data       := #{tx := TxBin} } = Msg,
                      #data{on_chain_id = ChanId} = D) ->
    try aetx_sign:deserialize_from_binary(TxBin) of
        SignedTx ->
            check_signed_update_ack_tx(SignedTx, Msg, D)
...
..

Stores the state

check_signed_update_ack_tx(SignedTx, Msg,
                           #data{ state = State
                                , opts = Opts
                                , op = #op_ack{tag = ?UPDATE} = Op} = D) ->
...
...
{ok, D#data{ state = aesc_offchain_state:set_signed_tx(
                                    SignedTx, Updates, State, OnChainTrees, OnChainEnv, Opts)
                        , log = log_msg(rcv, ?UPDATE_ACK, Msg, D#data.log)}};
...
...
1 Like