diff options
Diffstat (limited to 'src/testing/testing_api_cmd_refresh.c')
-rw-r--r-- | src/testing/testing_api_cmd_refresh.c | 315 |
1 files changed, 182 insertions, 133 deletions
diff --git a/src/testing/testing_api_cmd_refresh.c b/src/testing/testing_api_cmd_refresh.c index 2aad77ce3..111e9118f 100644 --- a/src/testing/testing_api_cmd_refresh.c +++ b/src/testing/testing_api_cmd_refresh.c @@ -75,12 +75,12 @@ struct TALER_TESTING_FreshCoinData * applicable. */ struct TALER_AgeCommitmentProof *age_commitment_proof; - struct TALER_AgeCommitmentHash *h_age_commitment; + struct TALER_AgeCommitmentHash h_age_commitment; /** * The blinding key (needed for recoup operations). */ - union TALER_DenominationBlindingKeyP blinding_key; + union GNUNET_CRYPTO_BlindingSecretP blinding_key; }; @@ -103,6 +103,11 @@ struct RefreshMeltState struct TALER_EXCHANGE_RefreshData refresh_data; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * Reference to a previous melt command. */ const char *melt_reference; @@ -113,6 +118,12 @@ struct RefreshMeltState struct TALER_EXCHANGE_MeltHandle *rmh; /** + * Expected entry in the coin history created by this + * operation. + */ + struct TALER_EXCHANGE_CoinHistoryEntry che; + + /** * Interpreter state. */ struct TALER_TESTING_Interpreter *is; @@ -140,6 +151,11 @@ struct RefreshMeltState const struct TALER_CoinSpendPrivateKeyP *melt_priv; /** + * Public key of the dirty coin being melted. + */ + struct TALER_CoinSpendPublicKeyP melt_pub; + + /** * Task scheduled to try later. */ struct GNUNET_SCHEDULER_Task *retry_task; @@ -210,6 +226,11 @@ struct RefreshRevealState struct TALER_EXCHANGE_RefreshesRevealHandle *rrh; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * Convenience struct to keep in one place all the * data related to one fresh coin, set by the reveal callback * as it comes from the exchange. @@ -273,6 +294,11 @@ struct RefreshLinkState const char *reveal_reference; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * Handle to the ongoing operation. */ struct TALER_EXCHANGE_LinkHandle *rlh; @@ -334,8 +360,7 @@ do_reveal_retry (void *cls) struct RefreshRevealState *rrs = cls; rrs->retry_task = NULL; - rrs->is->commands[rrs->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (rrs->is); refresh_reveal_run (rrs, NULL, rrs->is); @@ -380,24 +405,16 @@ reveal_cb (void *cls, MAX_BACKOFF); rrs->total_backoff = GNUNET_TIME_relative_add (rrs->total_backoff, rrs->backoff); - rrs->is->commands[rrs->is->ip].num_tries++; + TALER_TESTING_inc_tries (rrs->is); rrs->retry_task = GNUNET_SCHEDULER_add_delayed (rrs->backoff, &do_reveal_retry, rrs); return; } } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - rrs->is->commands[rrs->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rrs->is); + TALER_TESTING_unexpected_status (rrs->is, + hr->http_status, + rrs->expected_response_code); return; } melt_cmd = TALER_TESTING_interpreter_lookup_command (rrs->is, @@ -411,7 +428,7 @@ reveal_cb (void *cls, switch (hr->http_status) { case MHD_HTTP_OK: - rrs->num_fresh_coins = rr->details.success.num_coins; + rrs->num_fresh_coins = rr->details.ok.num_coins; rrs->psa = GNUNET_new_array (rrs->num_fresh_coins, struct TALER_PlanchetMasterSecretP); rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins, @@ -419,7 +436,7 @@ reveal_cb (void *cls, for (unsigned int i = 0; i<rrs->num_fresh_coins; i++) { const struct TALER_EXCHANGE_RevealedCoinInfo *coin - = &rr->details.success.coins[i]; + = &rr->details.ok.coins[i]; struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i]; rrs->psa[i] = coin->ps; @@ -434,19 +451,24 @@ reveal_cb (void *cls, return; } fc->coin_priv = coin->coin_priv; - fc->age_commitment_proof = coin->age_commitment_proof; - fc->h_age_commitment = coin->h_age_commitment; - TALER_denom_sig_deep_copy (&fc->sig, - &coin->sig); + if (NULL != coin->age_commitment_proof) + { + fc->age_commitment_proof = + TALER_age_commitment_proof_duplicate (coin->age_commitment_proof); + fc->h_age_commitment = coin->h_age_commitment; + } + + TALER_denom_sig_copy (&fc->sig, + &coin->sig); } if (0 != rrs->total_backoff.rel_value_us) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total reveal backoff for %s was %s\n", - rrs->is->commands[rrs->is->ip].label, + rrs->cmd->label, GNUNET_STRINGS_relative_time_to_string (rrs->total_backoff, - GNUNET_YES)); + true)); } break; default: @@ -488,6 +510,7 @@ refresh_reveal_run (void *cls, struct RefreshMeltState *rms; const struct TALER_TESTING_Command *melt_cmd; + rrs->cmd = cmd; rrs->is = is; melt_cmd = TALER_TESTING_interpreter_lookup_command (is, rrs->melt_reference); @@ -504,14 +527,16 @@ refresh_reveal_run (void *cls, for (unsigned int i = 0; i<rms->num_fresh_coins; i++) alg_values[i] = rms->mbds[i].alg_value; - rrs->rrh = TALER_EXCHANGE_refreshes_reveal (is->exchange, - &rms->rms, - &rms->refresh_data, - rms->num_fresh_coins, - alg_values, - rms->noreveal_index, - &reveal_cb, - rrs); + rrs->rrh = TALER_EXCHANGE_refreshes_reveal ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + &rms->rms, + &rms->refresh_data, + rms->num_fresh_coins, + alg_values, + rms->noreveal_index, + &reveal_cb, + rrs); } if (NULL == rrs->rrh) { @@ -538,10 +563,8 @@ refresh_reveal_cleanup (void *cls, (void) cmd; if (NULL != rrs->rrh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rrs->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (rrs->is, + cmd->label); TALER_EXCHANGE_refreshes_reveal_cancel (rrs->rrh); rrs->rrh = NULL; } @@ -552,7 +575,11 @@ refresh_reveal_cleanup (void *cls, } for (unsigned int j = 0; j < rrs->num_fresh_coins; j++) + { TALER_denom_sig_free (&rrs->fresh_coins[j].sig); + TALER_age_commitment_proof_free (rrs->fresh_coins[j].age_commitment_proof); + GNUNET_free (rrs->fresh_coins[j].age_commitment_proof); + } GNUNET_free (rrs->fresh_coins); GNUNET_free (rrs->psa); @@ -585,8 +612,7 @@ do_link_retry (void *cls) struct RefreshLinkState *rls = cls; rls->retry_task = NULL; - rls->is->commands[rls->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (rls->is); refresh_link_run (rls, NULL, rls->is); @@ -608,7 +634,6 @@ link_cb (void *cls, struct RefreshLinkState *rls = cls; const struct TALER_EXCHANGE_HttpResponse *hr = &lr->hr; const struct TALER_TESTING_Command *reveal_cmd; - struct TALER_TESTING_Command *link_cmd = &rls->is->commands[rls->is->ip]; unsigned int found; const unsigned int *num_fresh_coins; @@ -634,24 +659,16 @@ link_cb (void *cls, MAX_BACKOFF); rls->total_backoff = GNUNET_TIME_relative_add (rls->total_backoff, rls->backoff); - rls->is->commands[rls->is->ip].num_tries++; + TALER_TESTING_inc_tries (rls->is); rls->retry_task = GNUNET_SCHEDULER_add_delayed (rls->backoff, &do_link_retry, rls); return; } } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - link_cmd->label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rls->is); + TALER_TESTING_unexpected_status (rls->is, + hr->http_status, + rls->expected_response_code); return; } reveal_cmd = TALER_TESTING_interpreter_lookup_command (rls->is, @@ -675,11 +692,11 @@ link_cb (void *cls, TALER_TESTING_interpreter_fail (rls->is); return; } - if (lr->details.success.num_coins != *num_fresh_coins) + if (lr->details.ok.num_coins != *num_fresh_coins) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected number of fresh coins: %d vs %d in %s:%u\n", - lr->details.success.num_coins, + lr->details.ok.num_coins, *num_fresh_coins, __FILE__, __LINE__); @@ -687,11 +704,11 @@ link_cb (void *cls, return; } /* check that the coins match */ - for (unsigned int i = 0; i<lr->details.success.num_coins; i++) - for (unsigned int j = i + 1; j<lr->details.success.num_coins; j++) + for (unsigned int i = 0; i<lr->details.ok.num_coins; i++) + for (unsigned int j = i + 1; j<lr->details.ok.num_coins; j++) if (0 == - GNUNET_memcmp (&lr->details.success.coins[i].coin_priv, - &lr->details.success.coins[j].coin_priv)) + GNUNET_memcmp (&lr->details.ok.coins[i].coin_priv, + &lr->details.ok.coins[j].coin_priv)) GNUNET_break (0); /* Note: coins might be legitimately permutated in here... */ found = 0; @@ -709,12 +726,12 @@ link_cb (void *cls, return; } - for (unsigned int i = 0; i<lr->details.success.num_coins; i++) + for (unsigned int i = 0; i<lr->details.ok.num_coins; i++) { const struct TALER_EXCHANGE_LinkedCoinInfo *lci_i - = &lr->details.success.coins[i]; + = &lr->details.ok.coins[i]; - for (unsigned int j = 0; j<lr->details.success.num_coins; j++) + for (unsigned int j = 0; j<lr->details.ok.num_coins; j++) { const struct TALER_TESTING_FreshCoinData *fcj = &(*fc)[j]; @@ -735,12 +752,12 @@ link_cb (void *cls, } /* for j*/ } /* for i */ } - if (found != lr->details.success.num_coins) + if (found != lr->details.ok.num_coins) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Only %u/%u coins match expectations\n", found, - lr->details.success.num_coins); + lr->details.ok.num_coins); GNUNET_break (0); TALER_TESTING_interpreter_fail (rls->is); return; @@ -749,9 +766,9 @@ link_cb (void *cls, { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total link backoff for %s was %s\n", - rls->is->commands[rls->is->ip].label, + rls->cmd->label, GNUNET_STRINGS_relative_time_to_string (rls->total_backoff, - GNUNET_YES)); + true)); } break; default: @@ -782,9 +799,16 @@ refresh_link_run (void *cls, const struct TALER_TESTING_Command *reveal_cmd; const struct TALER_TESTING_Command *melt_cmd; const struct TALER_TESTING_Command *coin_cmd; + const char *exchange_url; - (void) cmd; + rls->cmd = cmd; rls->is = is; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } reveal_cmd = TALER_TESTING_interpreter_lookup_command (rls->is, rls->reveal_reference); if (NULL == reveal_cmd) @@ -827,11 +851,13 @@ refresh_link_run (void *cls, } /* finally, use private key from withdraw sign command */ - rls->rlh = TALER_EXCHANGE_link (is->exchange, - coin_priv, - rms->refresh_data.melt_age_commitment_proof, - &link_cb, - rls); + rls->rlh = TALER_EXCHANGE_link ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + coin_priv, + rms->refresh_data.melt_age_commitment_proof, + &link_cb, + rls); if (NULL == rls->rlh) { @@ -857,11 +883,8 @@ refresh_link_cleanup (void *cls, if (NULL != rls->rlh) { - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rls->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (rls->is, + cmd->label); TALER_EXCHANGE_link_cancel (rls->rlh); rls->rlh = NULL; } @@ -885,8 +908,7 @@ do_melt_retry (void *cls) struct RefreshMeltState *rms = cls; rms->retry_task = NULL; - rms->is->commands[rms->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (rms->is); melt_run (rms, NULL, rms->is); @@ -930,58 +952,56 @@ melt_cb (void *cls, MAX_BACKOFF); rms->total_backoff = GNUNET_TIME_relative_add (rms->total_backoff, rms->backoff); - rms->is->commands[rms->is->ip].num_tries++; + TALER_TESTING_inc_tries (rms->is); rms->retry_task = GNUNET_SCHEDULER_add_delayed (rms->backoff, &do_melt_retry, rms); return; } } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - rms->is->commands[rms->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rms->is); + TALER_TESTING_unexpected_status_with_body (rms->is, + hr->http_status, + rms->expected_response_code, + hr->reply); return; } if (MHD_HTTP_OK == hr->http_status) { - rms->noreveal_index = mr->details.success.noreveal_index; - if (mr->details.success.num_mbds != rms->num_fresh_coins) + rms->noreveal_index = mr->details.ok.noreveal_index; + if (mr->details.ok.num_mbds != rms->num_fresh_coins) { GNUNET_break (0); TALER_TESTING_interpreter_fail (rms->is); return; } GNUNET_free (rms->mbds); - rms->mbds = GNUNET_memdup (mr->details.success.mbds, - mr->details.success.num_mbds - * sizeof (struct - TALER_EXCHANGE_MeltBlindingDetail)); + rms->mbds = GNUNET_new_array ( + mr->details.ok.num_mbds, + struct TALER_EXCHANGE_MeltBlindingDetail); + for (unsigned int i = 0; i<mr->details.ok.num_mbds; i++) + TALER_denom_ewv_copy (&rms->mbds[i].alg_value, + &mr->details.ok.mbds[i].alg_value); } if (0 != rms->total_backoff.rel_value_us) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total melt backoff for %s was %s\n", - rms->is->commands[rms->is->ip].label, + rms->cmd->label, GNUNET_STRINGS_relative_time_to_string (rms->total_backoff, - GNUNET_YES)); + true)); } if (rms->double_melt) { TALER_LOG_DEBUG ("Doubling the melt (%s)\n", - rms->is->commands[rms->is->ip].label); - rms->rmh = TALER_EXCHANGE_melt (rms->is->exchange, - &rms->rms, - &rms->refresh_data, - &melt_cb, - rms); + rms->cmd->label); + rms->rmh = TALER_EXCHANGE_melt ( + TALER_TESTING_interpreter_get_context (rms->is), + TALER_TESTING_get_exchange_url (rms->is), + TALER_TESTING_get_keys (rms->is), + &rms->rms, + &rms->refresh_data, + &melt_cb, + rms); rms->double_melt = false; return; } @@ -1009,7 +1029,7 @@ melt_run (void *cls, }; const char **melt_fresh_amounts; - (void) cmd; + rms->cmd = cmd; if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts)) melt_fresh_amounts = default_melt_fresh_amounts; rms->is = is; @@ -1026,12 +1046,12 @@ melt_run (void *cls, { struct TALER_Amount melt_amount; struct TALER_Amount fresh_amount; - const struct TALER_AgeCommitmentProof *age_commitment_proof; - const struct TALER_AgeCommitmentHash *h_age_commitment; + const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL; + const struct TALER_AgeCommitmentHash *h_age_commitment = NULL; const struct TALER_DenominationSignature *melt_sig; const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub; const struct TALER_TESTING_Command *coin_command; - bool age_restricted; + bool age_restricted_denom; if (NULL == (coin_command = TALER_TESTING_interpreter_lookup_command ( @@ -1052,7 +1072,6 @@ melt_run (void *cls, TALER_TESTING_interpreter_fail (rms->is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_age_commitment_proof (coin_command, 0, @@ -1072,7 +1091,6 @@ melt_run (void *cls, TALER_TESTING_interpreter_fail (rms->is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig (coin_command, 0, @@ -1082,7 +1100,6 @@ melt_run (void *cls, TALER_TESTING_interpreter_fail (rms->is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub (coin_command, 0, @@ -1096,7 +1113,10 @@ melt_run (void *cls, /* Melt amount starts with the melt fee of the old coin; we'll add the values and withdraw fees of the fresh coins next */ melt_amount = melt_denom_pub->fees.refresh; - age_restricted = melt_denom_pub->key.age_mask.bits != 0; + age_restricted_denom = melt_denom_pub->key.age_mask.bits != 0; + GNUNET_assert (age_restricted_denom == (NULL != age_commitment_proof)); + GNUNET_assert ((NULL == age_commitment_proof) || + (0 < age_commitment_proof->commitment.num)); for (unsigned int i = 0; i<num_fresh_coins; i++) { const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk; @@ -1113,9 +1133,9 @@ melt_run (void *cls, TALER_TESTING_interpreter_fail (rms->is); return; } - fresh_pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange), + fresh_pk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (rms->is), &fresh_amount, - age_restricted); + age_restricted_denom); if (NULL == fresh_pk) { GNUNET_break (0); @@ -1133,27 +1153,46 @@ melt_run (void *cls, &fresh_pk->fees.withdraw)); rms->fresh_pks[i] = *fresh_pk; /* Make a deep copy of the RSA key */ - TALER_denom_pub_deep_copy (&rms->fresh_pks[i].key, - &fresh_pk->key); + TALER_denom_pub_copy (&rms->fresh_pks[i].key, + &fresh_pk->key); } /* end for */ rms->refresh_data.melt_priv = *rms->melt_priv; + GNUNET_CRYPTO_eddsa_key_get_public (&rms->melt_priv->eddsa_priv, + &rms->melt_pub.eddsa_pub); rms->refresh_data.melt_amount = melt_amount; rms->refresh_data.melt_sig = *melt_sig; rms->refresh_data.melt_pk = *melt_denom_pub; - rms->refresh_data.melt_age_commitment_proof = age_commitment_proof; - rms->refresh_data.melt_h_age_commitment = h_age_commitment; + + if (NULL != age_commitment_proof) + { + GNUNET_assert (NULL != h_age_commitment); + rms->refresh_data.melt_age_commitment_proof = age_commitment_proof; + rms->refresh_data.melt_h_age_commitment = h_age_commitment; + } rms->refresh_data.fresh_pks = rms->fresh_pks; rms->refresh_data.fresh_pks_len = num_fresh_coins; - GNUNET_assert (age_restricted == + GNUNET_assert (age_restricted_denom == (NULL != age_commitment_proof)); - - rms->rmh = TALER_EXCHANGE_melt (is->exchange, - &rms->rms, - &rms->refresh_data, - &melt_cb, - rms); + GNUNET_assert ((NULL == age_commitment_proof) || + (0 < age_commitment_proof->commitment.num)); + + rms->che.type = TALER_EXCHANGE_CTT_MELT; + rms->che.amount = melt_amount; + if (NULL != age_commitment_proof) + rms->che.details.melt.h_age_commitment = *h_age_commitment; + else + rms->che.details.melt.no_hac = true; + + rms->rmh = TALER_EXCHANGE_melt ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + &rms->rms, + &rms->refresh_data, + &melt_cb, + rms); if (NULL == rms->rmh) { @@ -1181,10 +1220,8 @@ melt_cleanup (void *cls, (void) cmd; if (NULL != rms->rmh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rms->is->ip, - rms->is->commands[rms->is->ip].label); + TALER_TESTING_command_incomplete (rms->is, + cmd->label); TALER_EXCHANGE_melt_cancel (rms->rmh); rms->rmh = NULL; } @@ -1199,7 +1236,12 @@ melt_cleanup (void *cls, TALER_denom_pub_free (&rms->fresh_pks[i].key); GNUNET_free (rms->fresh_pks); } - GNUNET_free (rms->mbds); + if (NULL != rms->mbds) + { + for (unsigned int i = 0; i < rms->num_fresh_coins; i++) + TALER_denom_ewv_free (&rms->mbds[i].alg_value); + GNUNET_free (rms->mbds); + } GNUNET_free (rms->melt_fresh_amounts); GNUNET_free (rms); } @@ -1231,17 +1273,23 @@ melt_traits (void *cls, struct TALER_TESTING_Trait traits[] = { TALER_TESTING_make_trait_denom_pub (index, &rms->fresh_pks[index]), - TALER_TESTING_make_trait_coin_priv (index, + TALER_TESTING_make_trait_coin_priv (0, rms->melt_priv), + TALER_TESTING_make_trait_coin_pub (0, + &rms->melt_pub), + TALER_TESTING_make_trait_coin_history (0, + &rms->che), TALER_TESTING_make_trait_age_commitment_proof ( index, rms->refresh_data.melt_age_commitment_proof), TALER_TESTING_make_trait_h_age_commitment ( index, rms->refresh_data.melt_h_age_commitment), - TALER_TESTING_make_trait_exchange_wd_value (index, - &rms->mbds[index].alg_value), TALER_TESTING_make_trait_refresh_secret (&rms->rms), + (NULL != rms->mbds) + ? TALER_TESTING_make_trait_exchange_wd_value (index, + &rms->mbds[index].alg_value) + : TALER_TESTING_trait_end (), TALER_TESTING_trait_end () }; @@ -1408,7 +1456,7 @@ refresh_reveal_traits (void *cls, rrs->fresh_coins[index].age_commitment_proof), TALER_TESTING_make_trait_h_age_commitment ( index, - rrs->fresh_coins[index].h_age_commitment), + &rrs->fresh_coins[index].h_age_commitment), TALER_TESTING_make_trait_denom_pub ( index, rrs->fresh_coins[index].pk), @@ -1426,6 +1474,7 @@ refresh_reveal_traits (void *cls, &rrs->psa[index]), TALER_TESTING_trait_end () }; + return TALER_TESTING_get_trait (traits, ret, trait, |