-
Notifications
You must be signed in to change notification settings - Fork 239
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
Implement GA awareness of State Channel's FSM #2502
Conversation
Supersedes #2483. |
d50c4f3
to
23058c9
Compare
@@ -244,7 +244,7 @@ tx_event(Name, #env{events = Events} = Env) -> | |||
none -> Env; | |||
{value, SignedTx} -> | |||
TxHash = aetx_sign:hash(SignedTx), | |||
{Type, _} = aetx:specialize_type(aetx_sign:tx(SignedTx)), | |||
{Type, _} = aetx:specialize_type(aetx_sign:innermost_tx(SignedTx)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is made GA aware. For transactions wrappend in a meta authentication transaction, the old processing was emitting not channel's type but rather ga_meta_tx
instead.
This is not breaking consesus but rather modifying the emitted events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on inspection of the code this shall make the aesc_fsm_min_depth_watcher
work better for the has_tx
scenario.
, tx_event_op({channel, aesc_channels:pubkey(InitiatorPubkey, | ||
Nonce, | ||
ResponderPubkey)}) | ||
, tx_event_op({channel, ChannelPubkey}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is made GA aware. For channel create transactions wrappend in a meta authentication transaction(s) the channel pubkey is computed in a different GA-specific way (the nonce is 0, so we use the auth_id
instead). This was emitting events with the wrong channel ID.
This is not breaking consesus but rather modifying the emitted events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This solves: Check event emited when GA channel is created
|
||
case aesc_utils:channel_pubkey(SignedTx) of | ||
{error, not_channel_tx} -> | ||
lager:debug("Tx=~p is not a channel transaction", [SignedTx]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we really include the whole SignedTx
in the debug printout?
responder_is_ga, both_are_ga]) of | ||
true -> | ||
_Config1 = stop_node(dev1, Config); | ||
false -> pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When does this happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a bit too implicit: the groups that belong to generalized_accounts
group - namely - initator_is_ga
, responder_is_ga
and both_are_ga
start with setting up one or two generalized accounts. Usually nodes are being stopped at each group end but since we can save some contracts upgrading: we don't stop the node when finishing any of the following groups:
ga_sequence() ->
[ {group, transactions}
, {group, errors}
, {group, signatures}
, {group, channel_ids}
].
To answer your question: while finishing transactions
group for example:
- when being ran outside of the scope of
generalized_accounts
group - there is noga_group
being set, so we stop the node (to start it with the next group) - when being ran in the context of any of the
generalized_accounts
groups - we do not stop the node (so we don't lose the GA setup). Instead we stop it once finishing the group
% ConKey = aect_contracts:compute_contract_pubkey(Owner, Nonce), | ||
% CallKey = aect_call:id(Owner, Nonce, ConKey), | ||
% CallTree = aect_test_utils:calls(S1), | ||
% Call = aect_call_state_tree:get_call(ConKey, CallKey, CallTree), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete commented code?
ok = rpc(dev1, aec_tx_pool, push, [SignedTx]), | ||
wait_for_signed_transaction_in_block(dev1, SignedTx), | ||
|
||
% ConKey = aect_contracts:compute_contract_pubkey(Owner, Nonce), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
% ConKey = aect_contracts:compute_contract_pubkey(Owner, Nonce), |
wait_for_signed_transaction_in_block(dev1, SignedTx), | ||
|
||
% ConKey = aect_contracts:compute_contract_pubkey(Owner, Nonce), | ||
% CallKey = aect_call:id(Owner, Nonce, ConKey), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
% CallKey = aect_call:id(Owner, Nonce, ConKey), |
|
||
% ConKey = aect_contracts:compute_contract_pubkey(Owner, Nonce), | ||
% CallKey = aect_call:id(Owner, Nonce, ConKey), | ||
% CallTree = aect_test_utils:calls(S1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
% CallTree = aect_test_utils:calls(S1), |
% ConKey = aect_contracts:compute_contract_pubkey(Owner, Nonce), | ||
% CallKey = aect_call:id(Owner, Nonce, ConKey), | ||
% CallTree = aect_test_utils:calls(S1), | ||
% Call = aect_call_state_tree:get_call(ConKey, CallKey, CallTree), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
% Call = aect_call_state_tree:get_call(ConKey, CallKey, CallTree), |
23058c9
to
1fce86c
Compare
Related doc (so we remember):
|
{ok, TxBin} = aeser_api_encoder:safe_decode(transaction, EncTx), | ||
Tx = aetx:deserialize_from_binary(TxBin), | ||
SignedTx = | ||
case account_type(Pubkey) of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this is changing behaviour of the test depending on the behaviour of the system under test - chain. I see that when test makes account generic it mines with strictly_follow_top
. Seems good enough. Ok.
@@ -5299,24 +5324,13 @@ sc_ws_timeout_open_(Config) -> | |||
sc_ws_attach_initiator(Config) -> | |||
#{initiator := #{pub_key := Pubkey, | |||
priv_key := Privkey}} = proplists:get_value(participants, Config), | |||
attach({Pubkey, Privkey}, "simple_auth", "authorize", ["42"]), | |||
attach({Pubkey, Privkey}, "simple_auth", "authorize", [?SIMPLE_AUTH_GA_SECRET]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please consider PR #2510 - it aims at polishing this area of the test.
a0c28c7
to
49506df
Compare
I am still to provide a PR in the |
@@ -153,7 +155,8 @@ process(#channel_create_tx{} = Tx, Trees, Env) -> | |||
lock_period(Tx), | |||
fee(Tx), | |||
nonce(Tx), | |||
round(Tx)), | |||
round(Tx), | |||
ChannelPubkey), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is independent refactoring and not strictly needed, right?
It is ok to keep here - I am asking simply because I am considering regression testing across tags using this module as interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without this, the wrong channel_id
event is being sent for GAs - if the initiator
is a generalized account one's nonce is not being used in channel_id
computation. This fixes it
case aetx:specialize_callback(aetx_sign:tx(SignedTx)) of | ||
{aega_meta_tx, MetaTx} -> | ||
case aega_meta_tx:ga_pubkey(MetaTx) =:= Signer of | ||
true -> aega_meta_tx:auth_id(MetaTx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to take the auth id of outermost ga meta tx related to the account, while an on-chain processing of tx takes the innermost one (see aega_meta_tx:process
). Is this intended?
I am still getting my head around this, and this may have no impact if multiple ga meta tx for same account prevented somewhere else. Though there may be room for opportunity in readability (maybe in other point in code) hence this note.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Different transaction types have different origin
and the origin
of channel_create_transaction
is the initiator
. This function is intended to get the initiator
's nonce or auth_id out from channel_create_transaction
so it can later on be used in channel_id
computation. In case that the outermost transaction indeed is a meta one, it could be either the initiator
or the responder
authentication method, thus the aega_meta_tx:ga_pubkey(MetaTx) =:= Signer
check - we return only the Signer
's auth_id
.
What botters me now is the other option - if none of the participants is a GA, the funciton returns Mod:nonce(Tx)
. This could return unexpected result if the get_nonce_or_auth_id/2
function is called for the responder
instead.
I am to rework and rename it to express more clearly the intention regarding the transaction type and the participant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in bf327a8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change is improvement thanks.
Doubt re outermost-vs-innermost still stays. For the same account-meant-to-be-authenticated, multiple ga meta txs can be nested within each other.
verify_signatures_onchain(SignedTx, Trees, Env) -> | ||
verify_signatures_onchain(SignedTx, Trees, Env, []). | ||
|
||
verify_signatures_onchain(SignedTx, Trees, Env, CheckedSigners) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I arrived here from clause awaiting_signature(cast, {?SIGNED, create_tx, Tx} = Msg,
in the FSM.
I have the impression that this function does not prevent not relevant extra ga meta txs - i.e. ga meta tx authenticating accounts not relevant to operation. Though I see that this is checked on chain, and FSM waits for tx in chain anyway. So ok.
Though an assertion aesc_utils:count_authentications(...) =:= 1
would improve readability in code and reduce chance of user(s?) preparing wrong tx. Maybe could be considered as future improvement (outside of this PR).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly for code path from half_signed(cast, {?FND_SIGNED, Msg}, #data{role = initiator,
. Though ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to check combine_sigs_or_meta
.
Pending comment: outermost-vs-innermost ga meta tx auth id.
I prepared a couple of minor improvements in #2516 - totally optional.
apps/aechannel/src/aesc_fsm.erl
Outdated
end. | ||
|
||
combine_sigs_or_meta(HalfSigned1, HalfSigned2) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that a correct ga auth fun should enquire the inner tx hash, otherwise the auth can be used for forging other tx.
(Trying to understand how this combining works.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to check
combine_sigs_or_meta
.
I did not check it.
Pending comment: outermost-vs-innermost ga meta tx auth id.
If multiple meta tx for same account, I expect effect is that the watcher does not observe right tx on chain. Though I expect same effect may happen in other way (e.g. I expect party receiving half signed tx to have option to publish to mempool tx different from the one later provided by other party). I expect party doing so would not gain anything... apart from locking the funds of the other party until he/she realizes.
I have the impression that this function does not prevent not relevant extra ga meta txs - i.e. ga meta tx authenticating accounts not relevant to operation. Though I see that this is checked on chain, and FSM waits for tx in chain anyway. So ok.
Though an assertion
aesc_utils:count_authentications(...) =:= 1
would improve readability in code and reduce chance of user(s?) preparing wrong tx. Maybe could be considered as future improvement (outside of this PR).
If this restriction were introduced maybe the code would be easier to follow - though make nested meta tx for same account not allowed. Out of scope.
630efff
to
098a210
Compare
098a210
to
54367b1
Compare
catch _:_ -> | ||
aetx_sign:new(InnerTx0, []) | ||
end, | ||
Options1 = maps:merge(#{%fee => 50000 * aec_test_utils:min_gas_price(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall this comment be deleted?
apps/aehttp/test/aehttp_sc_SUITE.erl
Outdated
AuthTx = | ||
case aetx:specialize_type(InnerMostTx) of | ||
{channel_offchain_tx, _} -> InnerMostTx; | ||
_ -> InnerTx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally optional. I find it more readable in this way.
_ -> InnerTx | |
{_, _} -> InnerTx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please confirm that aest_channels_SUITE
runs? I would not be surprised if some s/tx/signed_tx/g needed.
@@ -936,55 +940,49 @@ awaiting_signature(cast, {?SIGNED, withdraw_tx, Tx} = Msg, | |||
awaiting_signature(cast, {?SIGNED, ?FND_CREATED, SignedTx} = Msg, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels that the macros like FND_CREATED
in aesc_codec.hrl (-define(FND_CREATED , funding_created).
) shall be shared with sc_ws_api_jsonrpc
- as part of encoding/decoding in user API.
Though orthogonal to this PR.
@@ -936,55 +940,49 @@ awaiting_signature(cast, {?SIGNED, withdraw_tx, Tx} = Msg, | |||
awaiting_signature(cast, {?SIGNED, ?FND_CREATED, SignedTx} = Msg, | |||
#data{role = responder, | |||
latest = {sign, ?FND_CREATED, HSCTx, Updates}} = D) -> | |||
NewSignedTx = aetx_sign:add_signatures( | |||
HSCTx, aetx_sign:signatures(SignedTx)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Who does now check that SignedTx
is related to HSCTx
rather than being a completely distinct transaction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the assumption maybe that the user of the user API is trusted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assumption is that if it is a different:
- new transaction - then authentication method will not match
- old co-signed transaction - the check round will catch it
We used to have the following flow:
This has changed to (differences are
Note that |
apps/aega/test/aega_test_utils.erl
Outdated
@@ -184,10 +184,14 @@ make_calldata(Code, Fun, Args) -> | |||
{ok, Calldata} = aeso_compiler:create_calldata(Code, Fun, Args, [{backend, Backend}]), | |||
Calldata. | |||
|
|||
get_contract(Name) -> | |||
get_contract(Name0) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why Name0
here? By convention, this is normally used when some preparatory operation converts it to Name
.
@@ -0,0 +1,8 @@ | |||
* Introduces State Channel FSM Generalized Accounts awareness. It now fully | |||
supports creation of meta transactions and verifies them. | |||
* Changes WebSocket API regarding signing transactions: now sign requests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read this line mentions the change in the user API. Ok.
@@ -0,0 +1,8 @@ | |||
* Introduces State Channel FSM Generalized Accounts awareness. It now fully | |||
supports creation of meta transactions and verifies them. | |||
* Changes WebSocket API regarding signing transactions: now sign requests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- the
FSM_B
sends the co-authenticated tx toFSM_B
(3)Note that
3
changes thenoise
protocol message and expectations for the message format itself, thus making it non-backwards compatible with older versions.
Shall there be somewhere in this file at least a mention of the channels Noise node-to-node protocol breakage/change?
Amended wiki with target release. |
SC FSM: GA awareness