Skip to content

Commit

Permalink
Add meta update object to SC offchain updates (#2636)
Browse files Browse the repository at this point in the history
* WIP Add meta update object to SC offchain updates

* Add meta support to WS API + test case in sc_SUITE

* Update deps after merging aeserialization change

* Support version_offchain_update option (also in system test)

* Fix whitespace error

* Fail gracefully if meta objs are used w wrong vsn
  • Loading branch information
uwiger committed Aug 22, 2019
1 parent 7fe0382 commit c44aa1d
Show file tree
Hide file tree
Showing 12 changed files with 440 additions and 201 deletions.
64 changes: 51 additions & 13 deletions apps/aechannel/src/aesc_fsm.erl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
, upd_create_contract/2 %%
, upd_deposit/2 %% (fsm() , map())
, upd_transfer/4 %% (fsm() , from(), to(), amount())
, upd_transfer/5 %% (fsm() , from(), to(), amount(), #{})
, upd_withdraw/2 %% (fsm() , map())
, where/2
]).
Expand All @@ -64,6 +65,7 @@
, record_fields/1
, report_tags/0
, timeouts/0
, version_tags/0
]).

%% Used by noise session
Expand Down Expand Up @@ -259,11 +261,14 @@ upd_deposit(Fsm, #{amount := Amt} = Opts) when is_integer(Amt) ->
lager:debug("upd_deposit(~p)", [Opts]),
gen_statem:call(Fsm, {upd_deposit, Opts}).

upd_transfer(_Fsm, _From, _To, Amount) when Amount < 0 ->
upd_transfer(Fsm, From, To, Amount) ->
upd_transfer(Fsm, From, To, Amount, #{}).

upd_transfer(_Fsm, _From, _To, Amount, _Opts) when Amount < 0 ->
{error, negative_amount};
upd_transfer(Fsm, From, To, Amount) when is_integer(Amount) ->
lager:debug("upd_transfer(~p, ~p, ~p, ~p)", [Fsm, From, To, Amount]),
gen_statem:call(Fsm, {upd_transfer, From, To, Amount}).
upd_transfer(Fsm, From, To, Amount, Opts) when is_integer(Amount), is_map(Opts) ->
lager:debug("upd_transfer(~p, ~p, ~p, ~p, ~p)", [Fsm, From, To, Amount, Opts]),
gen_statem:call(Fsm, {upd_transfer, From, To, Amount, Opts}).

upd_withdraw(_Fsm, #{amount := Amt}) when Amt < 0 ->
{error, negative_amount};
Expand Down Expand Up @@ -297,6 +302,9 @@ record_fields(Other) -> aesc_offchain_state:record_fields(Other).
report_tags() ->
maps:keys(?DEFAULT_REPORT_FLAGS).

version_tags() ->
[offchain_update].

timeouts() ->
maps:keys(?DEFAULT_TIMEOUTS).

Expand Down Expand Up @@ -2246,18 +2254,19 @@ check_update_ack_(SignedTx, HalfSignedTx) ->
lager:debug("Txes are the same", []),
ok.

handle_upd_transfer(FromPub, ToPub, Amount, From, #data{ state = State
, opts = Opts
, on_chain_id = ChannelId
} = D) ->
Updates = [aesc_offchain_update:op_transfer(aeser_id:create(account, FromPub),
aeser_id:create(account, ToPub), Amount)],
handle_upd_transfer(FromPub, ToPub, Amount, From, UOpts, #data{ state = State
, opts = Opts
, on_chain_id = ChannelId
} = D) ->
{OnChainEnv, OnChainTrees} = tx_env_and_trees_from_top(aetx_contract),
Height = aetx_env:height(OnChainEnv),
%% TODO PT-165214367: maybe set block_hash
BlockHash = ?NOT_SET_BLOCK_HASH,
ActiveProtocol = aec_hard_forks:protocol_effective_at_height(Height),
try
Updates = [aesc_offchain_update:op_transfer(aeser_id:create(account, FromPub),
aeser_id:create(account, ToPub), Amount)
| meta_updates(UOpts)],
Tx1 = aesc_offchain_state:make_update_tx(Updates, State, ChannelId, ActiveProtocol,
OnChainTrees, OnChainEnv, Opts),
case request_signing(?UPDATE, Tx1, Updates, BlockHash, D, defer) of
Expand All @@ -2275,6 +2284,11 @@ handle_upd_transfer(FromPub, ToPub, Amount, From, #data{ state = State
process_update_error(Reason, From, D)
end.

meta_updates(Opts) when is_map(Opts) ->
L = maps:get(meta, Opts, []),
[aesc_offchain_update:op_meta(D) || D <- L,
is_binary(D)].

send_leave_msg(#data{ on_chain_id = ChId
, session = Session} = Data) ->
Msg = #{ channel_id => ChId },
Expand Down Expand Up @@ -3060,7 +3074,8 @@ init(#{opts := Opts0} = Arg) ->
fun(O) -> check_minimum_depth_opt(DefMinDepth, Role, O) end,
fun check_timeout_opt/1,
fun check_rpt_opt/1,
fun check_log_opt/1
fun check_log_opt/1,
fun check_version_opts/1
], Opts2),
#{initiator := Initiator} = Opts,
Session = start_session(Arg, Reestablish, Opts#{role => Role}),
Expand Down Expand Up @@ -3157,6 +3172,29 @@ check_timeout_opt(#{timeouts := TOs} = Opts) ->
check_timeout_opt(Opts) ->
check_timeout_opt(Opts#{timeouts => #{}}).

check_version_opts(#{versions := S} = Opts) ->
case maps:fold(
fun(_, _, {error,_} = E) ->
E;
(offchain_update = Cat, V, ok) ->
try aesc_offchain_update:set_vsn(V)
catch
error:_ ->
{error, {invalid_vsn, Cat}}
end;
(Cat, _V, ok) ->
lager:debug("Unsupported version option ~p - ignoring", [Cat]),
ok
end, ok, S) of
ok ->
Opts;
{error, _} = Error ->
lager:error("Invalid serialization: ~p", [Error]),
maps:remove(versions, Opts)
end;
check_version_opts(Opts) ->
Opts.

check_rpt_opt(#{report := R} = Opts) when is_map(R) ->
L = [{K,V} || {K,V} <- maps:to_list(R),
lists:member(K, report_tags()) andalso is_boolean(V)],
Expand Down Expand Up @@ -3491,11 +3529,11 @@ handle_call_(_AnyState, {inband_msg, ToPub, Msg}, From, #data{} = D) ->
_ ->
keep_state(D, [{reply, From, {error, unknown_recipient}}])
end;
handle_call_(open, {upd_transfer, FromPub, ToPub, Amount}, From,
handle_call_(open, {upd_transfer, FromPub, ToPub, Amount, Opts}, From,
#data{opts = #{initiator := I, responder := R}} = D) ->
case FromPub =/= ToPub andalso ([] == [FromPub, ToPub] -- [I, R]) of
true ->
handle_upd_transfer(FromPub, ToPub, Amount, From, set_ongoing(D));
handle_upd_transfer(FromPub, ToPub, Amount, From, Opts, set_ongoing(D));
false ->
keep_state(set_ongoing(D), [{reply, From, {error, invalid_pubkeys}}])
end;
Expand Down
88 changes: 70 additions & 18 deletions apps/aechannel/src/aesc_offchain_update.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
-module(aesc_offchain_update).

-define(UPDATE_VSN, 1).
-define(UPDATE_VSN, 2).
-define(UPDATE_VSN_1, 1).
-define(UPDATE_VSN_1_OR_2(V), V =:= ?UPDATE_VSN_1; V =:= ?UPDATE_VSN).

-record(transfer, {
from_id :: aeser_id:id(),
Expand Down Expand Up @@ -38,14 +40,17 @@
gas :: non_neg_integer()
}).

-record(meta, { data :: binary() }).

-opaque update() :: #transfer{}
| #withdraw{}
| #deposit{}
| #create_contract{}
| #call_contract{}.
| #call_contract{}
| #meta{}.

-type update_type() :: transfer | withdraw | deposit | create_contract |
call_contract.
call_contract | meta.

-export_type([update/0]).

Expand All @@ -55,13 +60,16 @@
, op_new_contract/6
, op_call_contract/6
, op_call_contract/8
, op_meta/1
]).

-export([serialize/1,
deserialize/1,
for_client/1,
apply_on_trees/6]).

-export([set_vsn/1]).

-export([is_call/1,
is_contract_create/1,
extract_call/1,
Expand All @@ -88,6 +96,8 @@ from_db_format(#create_contract{} = U) ->
U;
from_db_format(#call_contract{} = U) ->
U;
from_db_format(#meta{} = U) ->
U;
from_db_format(Tuple) ->
OldType = element(1, Tuple),
NewType = maps:get(OldType, #{0 => transfer,
Expand All @@ -102,6 +112,7 @@ from_db_format(Tuple) ->
#deposit{} -> Updated;
#create_contract{} -> Updated;
#call_contract{} -> Updated;
#meta{} -> Updated;
_ ->
error(illegal_db_format)
end.
Expand Down Expand Up @@ -159,6 +170,10 @@ op_call_contract(CallerId, ContractId, ABIVersion, Amount, CallData, CallStack,
gas_price = GasPrice,
gas = Gas}.

op_meta(Data) ->
true = can_serialize(meta),
#meta{ data = Data }.

-spec apply_on_trees(aesc_offchain_update:update(), aec_trees:trees(),
aec_trees:trees(), aetx_env:env(),
non_neg_integer(), non_neg_integer()) -> aec_trees:trees().
Expand Down Expand Up @@ -201,7 +216,9 @@ apply_on_trees(Update, Trees0, OnChainTrees, OnChainEnv, Round, Reserve) ->
_Trees = aect_channel_contract:run(ContractPubKey, ABIVersion, Call,
CallData, CallStack,
Trees2, Amount, GasPrice, Gas,
OnChainTrees, OnChainEnv)
OnChainTrees, OnChainEnv);
#meta{} ->
Trees0
end.

-spec for_client(update()) -> map().
Expand Down Expand Up @@ -240,12 +257,31 @@ for_client(#call_contract{caller_id = CallerId, contract_id = ContractId,
<<"gas">> => Gas,
<<"gas_price">> => GasPrice,
<<"call_data">> => aeser_api_encoder:encode(contract_bytearray, CallData),
<<"call_stack">> => CallStack}.
<<"call_stack">> => CallStack};
for_client(#meta{data = Data}) ->
#{<<"op">> => type2swagger_name(meta),
<<"data">> => Data}.

update_vsn_key() ->
{?MODULE, update_vsn}.

get_update_vsn() ->
case get(update_vsn_key()) of
undefined ->
?UPDATE_VSN;
V ->
V
end.

set_vsn(V) when ?UPDATE_VSN_1_OR_2(V) ->
lager:debug("set serialization vsn to ~p", [V]),
put(update_vsn_key(), V),
ok.

-spec serialize(update()) -> binary().
serialize(Update) ->
Fields = update2fields(Update),
Vsn = ?UPDATE_VSN,
Vsn = get_update_vsn(),
UpdateType = record_to_update_type(Update),
aeser_chain_objects:serialize(
ut2type(UpdateType),
Expand Down Expand Up @@ -291,7 +327,9 @@ update2fields(#call_contract{caller_id = CallerId, contract_id = ContractId,
{gas, Gas},
{gas_price, GasPrice},
{call_data, CallData},
{call_stack, CallStack}].
{call_stack, CallStack}];
update2fields(#meta{data = Data}) ->
[ {data, Data} ].

-spec fields2update(update_type(), list()) -> update().
fields2update(transfer, [{from, From},
Expand Down Expand Up @@ -320,62 +358,70 @@ fields2update(call_contract, [ {caller, CallerId},
{call_data, CallData},
{call_stack, CallStack}]) ->
op_call_contract(CallerId, ContractId, ABIVersion, Amount, CallData,
CallStack, GasPrice, Gas).
CallStack, GasPrice, Gas);
fields2update(meta, [{data, Data}]) ->
op_meta(Data).

-spec ut2type(update_type()) -> atom().
ut2type(transfer) -> channel_offchain_update_transfer;
ut2type(deposit) -> channel_offchain_update_deposit;
ut2type(withdraw) -> channel_offchain_update_withdraw;
ut2type(create_contract) -> channel_offchain_update_create_contract;
ut2type(call_contract) -> channel_offchain_update_call_contract.
ut2type(call_contract) -> channel_offchain_update_call_contract;
ut2type(meta) -> channel_offchain_update_meta.

-spec type2ut(atom()) -> update_type().
type2ut(channel_offchain_update_transfer) -> transfer;
type2ut(channel_offchain_update_deposit) -> deposit;
type2ut(channel_offchain_update_withdraw) -> withdraw;
type2ut(channel_offchain_update_create_contract) -> create_contract;
type2ut(channel_offchain_update_call_contract) -> call_contract.
type2ut(channel_offchain_update_call_contract) -> call_contract;
type2ut(channel_offchain_update_meta) -> meta.

-spec type2swagger_name(update_type()) -> binary().
type2swagger_name(transfer) -> <<"OffChainTransfer">>;
type2swagger_name(deposit) -> <<"OffChainDeposit">>;
type2swagger_name(withdraw) -> <<"OffChainWithdrawal">>;
type2swagger_name(create_contract) -> <<"OffChainNewContract">>;
type2swagger_name(call_contract) -> <<"OffChainCallContract">>.
type2swagger_name(call_contract) -> <<"OffChainCallContract">>;
type2swagger_name(meta) -> <<"OffChainMeta">>.

-spec update_serialization_template(non_neg_integer(), update_type()) -> list().
update_serialization_template(?UPDATE_VSN, transfer) ->
update_serialization_template(V, transfer) when ?UPDATE_VSN_1_OR_2(V) ->
[ {from, id},
{to, id},
{amount, int}];
update_serialization_template(?UPDATE_VSN, deposit) ->
update_serialization_template(V, deposit) when ?UPDATE_VSN_1_OR_2(V) ->
[ {from, id},
{amount, int}];
update_serialization_template(?UPDATE_VSN, withdraw) ->
update_serialization_template(V, withdraw) when ?UPDATE_VSN_1_OR_2(V) ->
[ {to, id},
{amount, int}];
update_serialization_template(?UPDATE_VSN, create_contract) ->
update_serialization_template(V, create_contract) when ?UPDATE_VSN_1_OR_2(V) ->
[ {owner, id},
{ct_version, int},
{code, binary},
{deposit, int},
{call_data, binary}];
update_serialization_template(?UPDATE_VSN, call_contract) ->
update_serialization_template(V, call_contract) when ?UPDATE_VSN_1_OR_2(V) ->
[ {caller, id},
{contract, id},
{abi_version, int},
{amount, int},
{gas, int},
{gas_price, int},
{call_data, binary},
{call_stack, [int]}].
{call_stack, [int]}];
update_serialization_template(?UPDATE_VSN, meta) ->
[ {data, binary} ].

-spec record_to_update_type(update()) -> update_type().
record_to_update_type(#transfer{}) -> transfer;
record_to_update_type(#deposit{}) -> deposit;
record_to_update_type(#withdraw{}) -> withdraw;
record_to_update_type(#create_contract{}) -> create_contract;
record_to_update_type(#call_contract{}) -> call_contract.
record_to_update_type(#call_contract{}) -> call_contract;
record_to_update_type(#meta{}) -> meta.

check_min_amt(Amt, Reserve) ->
if Amt < Reserve ->
Expand Down Expand Up @@ -462,3 +508,9 @@ extract_abi_version(#call_contract{abi_version = ABIVersion}) ->

update_error(Err) ->
error({off_chain_update_error, Err}).

can_serialize(Op) ->
can_serialize(Op, get_update_vsn()).

can_serialize(meta, ?UPDATE_VSN_1) -> error(meta_not_allowed);
can_serialize(_ , _) -> true.

0 comments on commit c44aa1d

Please sign in to comment.