-work on p2p payments

This commit is contained in:
Christian Grothoff 2022-04-27 13:09:41 +02:00
parent 1396afc136
commit d623cab1a3
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
12 changed files with 182 additions and 47 deletions

View File

@ -329,7 +329,7 @@ parse_coin (struct MHD_Connection *connection,
if (GNUNET_OK != if (GNUNET_OK !=
TALER_wallet_purse_deposit_verify (TEH_base_url, TALER_wallet_purse_deposit_verify (TEH_base_url,
pcc->purse_pub, pcc->purse_pub,
&pcc->amount, &coin->amount,
&coin->cpi.coin_pub, &coin->cpi.coin_pub,
&coin->coin_sig)) &coin->coin_sig))
{ {
@ -549,11 +549,49 @@ TEH_handler_purses_deposit (
TALER_EC_GENERIC_PARAMETER_MALFORMED, TALER_EC_GENERIC_PARAMETER_MALFORMED,
"deposits"); "deposits");
} }
/* FIXME: fetch basic purse properties
(min age, purse_expiration, amount, merge_pub, {
h_contract_terms) from enum GNUNET_DB_QueryStatus qs;
DB. Generate 404 or 410 (Gone) replies if struct GNUNET_TIME_Timestamp merge_timestamp;
applicable. */
qs = TEH_plugin->select_purse (
TEH_plugin->cls,
pcc.purse_pub,
&pcc.purse_expiration,
&pcc.amount,
&pcc.deposit_total,
&pcc.h_contract_terms,
&merge_timestamp);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"select purse");
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"select purse");
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_GENERIC_PURSE_UNKNOWN,
NULL);
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
break; /* handled below */
}
if (GNUNET_TIME_absolute_is_past (pcc.purse_expiration.abs_time))
{
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_GONE,
TALER_EC_EXCHANGE_GENERIC_PURSE_EXPIRED,
NULL);
}
}
/* parse deposits */ /* parse deposits */
pcc.coins = GNUNET_new_array (pcc.num_coins, pcc.coins = GNUNET_new_array (pcc.num_coins,

View File

@ -384,7 +384,7 @@ TEH_handler_purses_merge (
return TALER_MHD_reply_with_error ( return TALER_MHD_reply_with_error (
connection, connection,
MHD_HTTP_NOT_FOUND, MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_MERGE_PURSE_NOT_FOUND, TALER_EC_EXCHANGE_GENERIC_PURSE_UNKNOWN,
NULL); NULL);
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
/* continued below */ /* continued below */

View File

@ -4004,14 +4004,14 @@ struct TALER_EXCHANGE_ContractGetResponse
struct TALER_PurseContractPublicKeyP purse_pub; struct TALER_PurseContractPublicKeyP purse_pub;
/** /**
* Private key of the merge capability. * Encrypted contract.
*/ */
struct TALER_PurseMergePrivateKeyP merge_priv; const void *econtract;
/** /**
* Contract terms. * Number of bytes in @e econtract.
*/ */
const json_t *contract_terms; size_t econtract_size;
} success; } success;

View File

@ -2417,6 +2417,7 @@ TALER_TESTING_cmd_purse_create_with_deposit (
* *
* @param label command label * @param label command label
* @param expected_http_status what HTTP status do we expect to get returned from the exchange * @param expected_http_status what HTTP status do we expect to get returned from the exchange
* @param for_merge true if for merge, false if for deposit
* @param contract_ref reference to a command providing us with the contract private key * @param contract_ref reference to a command providing us with the contract private key
* @return the command * @return the command
*/ */
@ -2424,6 +2425,7 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_contract_get ( TALER_TESTING_cmd_contract_get (
const char *label, const char *label,
unsigned int expected_http_status, unsigned int expected_http_status,
bool for_merge,
const char *contract_ref); const char *contract_ref);

View File

@ -106,7 +106,6 @@ handle_contract_get_finished (void *cls,
{ {
void *econtract; void *econtract;
size_t econtract_size; size_t econtract_size;
json_t *contract;
struct TALER_PurseContractSignatureP econtract_sig; struct TALER_PurseContractSignatureP econtract_sig;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("purse_pub", GNUNET_JSON_spec_fixed_auto ("purse_pub",
@ -143,24 +142,11 @@ handle_contract_get_finished (void *cls,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
break; break;
} }
contract = TALER_CRYPTO_contract_decrypt_for_merge ( dr.details.success.econtract = econtract;
&cgh->contract_priv, dr.details.success.econtract_size = econtract_size;
&dr.details.success.purse_pub,
econtract,
econtract_size,
&dr.details.success.merge_priv);
GNUNET_JSON_parse_free (spec);
if (NULL == contract)
{
GNUNET_break (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_EXCHANGE_CONTRACTS_DECRYPTION_FAILED;
break;
}
dr.details.success.contract_terms = contract;
cgh->cb (cgh->cb_cls, cgh->cb (cgh->cb_cls,
&dr); &dr);
json_decref (contract); GNUNET_JSON_parse_free (spec);
TALER_EXCHANGE_contract_get_cancel (cgh); TALER_EXCHANGE_contract_get_cancel (cgh);
return; return;
} }

View File

@ -281,6 +281,7 @@ TALER_EXCHANGE_purse_deposit (
const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i]; const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
json_t *jdeposit; json_t *jdeposit;
struct TALER_CoinSpendSignatureP coin_sig; struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_CoinSpendPublicKeyP coin_pub;
#if FIXME_OEC #if FIXME_OEC
struct TALER_AgeCommitmentHash agh; struct TALER_AgeCommitmentHash agh;
struct TALER_AgeCommitmentHash *aghp = NULL; struct TALER_AgeCommitmentHash *aghp = NULL;
@ -300,6 +301,8 @@ TALER_EXCHANGE_purse_deposit (
GNUNET_free (pch); GNUNET_free (pch);
return NULL; return NULL;
} }
GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv,
&coin_pub.eddsa_pub);
#endif #endif
TALER_wallet_purse_deposit_sign ( TALER_wallet_purse_deposit_sign (
url, url,
@ -322,6 +325,8 @@ TALER_EXCHANGE_purse_deposit (
&deposit->h_denom_pub), &deposit->h_denom_pub),
TALER_JSON_pack_denom_sig ("ub_sig", TALER_JSON_pack_denom_sig ("ub_sig",
&deposit->denom_sig), &deposit->denom_sig),
GNUNET_JSON_pack_data_auto ("coin_pub",
&coin_pub),
GNUNET_JSON_pack_data_auto ("coin_sig", GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig)); &coin_sig));
GNUNET_assert (0 == GNUNET_assert (0 ==

View File

@ -160,6 +160,7 @@ run (void *cls,
TALER_TESTING_cmd_contract_get ( TALER_TESTING_cmd_contract_get (
"push-get-contract", "push-get-contract",
MHD_HTTP_OK, MHD_HTTP_OK,
true, /* for merge */
"purse-with-deposit"), "purse-with-deposit"),
TALER_TESTING_cmd_purse_merge ( TALER_TESTING_cmd_purse_merge (
"purse-merge-into-reserve", "purse-merge-into-reserve",
@ -181,6 +182,7 @@ run (void *cls,
TALER_TESTING_cmd_contract_get ( TALER_TESTING_cmd_contract_get (
"pull-get-contract", "pull-get-contract",
MHD_HTTP_OK, MHD_HTTP_OK,
false, /* for deposit */
"purse-create-with-reserve"), "purse-create-with-reserve"),
TALER_TESTING_cmd_purse_deposit_coins ( TALER_TESTING_cmd_purse_deposit_coins (
"purse-deposit-coins", "purse-deposit-coins",

View File

@ -40,6 +40,11 @@ struct ContractGetState
*/ */
json_t *contract_terms; json_t *contract_terms;
/**
* Private key to decrypt the contract.
*/
struct TALER_ContractDiffiePrivateP contract_priv;
/** /**
* Set to the returned merge key. * Set to the returned merge key.
*/ */
@ -70,6 +75,12 @@ struct ContractGetState
*/ */
unsigned int expected_response_code; unsigned int expected_response_code;
/**
* True if this is for a 'merge' operation,
* 'false' if this is for a 'deposit' operation.
*/
bool merge;
}; };
@ -109,10 +120,42 @@ get_cb (void *cls,
const struct TALER_PurseMergePrivateKeyP *mp; const struct TALER_PurseMergePrivateKeyP *mp;
const json_t *ct; const json_t *ct;
/* check that we got what we expected to get! */ ds->purse_pub = dr->details.success.purse_pub;
if (GNUNET_OK != if (ds->merge)
TALER_TESTING_get_trait_merge_priv (ref, {
&mp)) if (GNUNET_OK !=
TALER_TESTING_get_trait_merge_priv (ref,
&mp))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is);
return;
}
ds->contract_terms =
TALER_CRYPTO_contract_decrypt_for_merge (
&ds->contract_priv,
&ds->purse_pub,
dr->details.success.econtract,
dr->details.success.econtract_size,
&ds->merge_priv);
if (0 !=
GNUNET_memcmp (mp,
&ds->merge_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is);
return;
}
}
else
{
ds->contract_terms =
TALER_CRYPTO_contract_decrypt_for_deposit (
&ds->contract_priv,
dr->details.success.econtract,
dr->details.success.econtract_size);
}
if (NULL == ds->contract_terms)
{ {
GNUNET_break (0); GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
@ -126,26 +169,14 @@ get_cb (void *cls,
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
return; return;
} }
if (0 !=
GNUNET_memcmp (mp,
&dr->details.success.merge_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is);
return;
}
if (1 != /* 1: equal, 0: not equal */ if (1 != /* 1: equal, 0: not equal */
json_equal (ct, json_equal (ct,
dr->details.success.contract_terms)) ds->contract_terms))
{ {
GNUNET_break (0); GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
return; return;
} }
ds->contract_terms = json_incref (
(json_t *) dr->details.success.contract_terms);
ds->merge_priv = dr->details.success.merge_priv;
ds->purse_pub = dr->details.success.purse_pub;
} }
TALER_TESTING_interpreter_next (ds->is); TALER_TESTING_interpreter_next (ds->is);
} }
@ -180,6 +211,7 @@ get_run (void *cls,
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
return; return;
} }
ds->contract_priv = *contract_priv;
ds->dh = TALER_EXCHANGE_contract_get ( ds->dh = TALER_EXCHANGE_contract_get (
is->exchange, is->exchange,
contract_priv, contract_priv,
@ -246,7 +278,8 @@ get_traits (void *cls,
TALER_TESTING_trait_end () TALER_TESTING_trait_end ()
}; };
return TALER_TESTING_get_trait (traits, /* skip 'merge_priv' if we are in 'merge' mode */
return TALER_TESTING_get_trait (&traits[ds->merge ? 0 : 1],
ret, ret,
trait, trait,
index); index);
@ -257,6 +290,7 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_contract_get ( TALER_TESTING_cmd_contract_get (
const char *label, const char *label,
unsigned int expected_http_status, unsigned int expected_http_status,
bool for_merge,
const char *contract_ref) const char *contract_ref)
{ {
struct ContractGetState *ds; struct ContractGetState *ds;
@ -264,6 +298,7 @@ TALER_TESTING_cmd_contract_get (
ds = GNUNET_new (struct ContractGetState); ds = GNUNET_new (struct ContractGetState);
ds->expected_response_code = expected_http_status; ds->expected_response_code = expected_http_status;
ds->contract_ref = contract_ref; ds->contract_ref = contract_ref;
ds->merge = for_merge;
{ {
struct TALER_TESTING_Command cmd = { struct TALER_TESTING_Command cmd = {
.cls = ds, .cls = ds,

View File

@ -154,9 +154,23 @@ deposit_run (void *cls,
{ {
struct PurseDepositState *ds = cls; struct PurseDepositState *ds = cls;
struct TALER_EXCHANGE_PurseDeposit deposits[ds->num_coin_references]; struct TALER_EXCHANGE_PurseDeposit deposits[ds->num_coin_references];
const struct TALER_PurseContractPublicKeyP *purse_pub;
const struct TALER_TESTING_Command *purse_cmd;
(void) cmd; (void) cmd;
ds->is = is; ds->is = is;
purse_cmd = TALER_TESTING_interpreter_lookup_command (is,
ds->purse_ref);
if (GNUNET_OK !=
TALER_TESTING_get_trait_purse_pub (purse_cmd,
&purse_pub))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
ds->purse_pub = *purse_pub;
for (unsigned int i = 0; i<ds->num_coin_references; i++) for (unsigned int i = 0; i<ds->num_coin_references; i++)
{ {
const struct Coin *cr = &ds->coin_references[i]; const struct Coin *cr = &ds->coin_references[i];

View File

@ -147,8 +147,9 @@ test_amount_LDADD = \
test_crypto_SOURCES = \ test_crypto_SOURCES = \
test_crypto.c test_crypto.c
test_crypto_LDADD = \ test_crypto_LDADD = \
libtalerutil.la \
-lgnunetutil \ -lgnunetutil \
libtalerutil.la -ljansson
test_payto_SOURCES = \ test_payto_SOURCES = \
test_payto.c test_payto.c

View File

@ -278,6 +278,9 @@ TALER_CRYPTO_contract_encrypt_for_merge (
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&nonce, &nonce,
sizeof (nonce)); sizeof (nonce));
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Encrypting with key %s\n",
TALER_B2S (&key));
contract_encrypt (&nonce, contract_encrypt (&nonce,
&key, &key,
sizeof (key), sizeof (key),
@ -315,6 +318,9 @@ TALER_CRYPTO_contract_decrypt_for_merge (
GNUNET_break (0); GNUNET_break (0);
return NULL; return NULL;
} }
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Decrypting with key %s\n",
TALER_B2S (&key));
if (GNUNET_OK != if (GNUNET_OK !=
contract_decrypt (&key, contract_decrypt (&key,
sizeof (key), sizeof (key),

View File

@ -438,6 +438,50 @@ test_merchant_sigs (void)
} }
static int
test_contracts (void)
{
struct TALER_ContractDiffiePrivateP cpriv;
struct TALER_PurseContractPublicKeyP purse_pub;
struct TALER_PurseContractPrivateKeyP purse_priv;
void *econtract;
size_t econtract_size;
struct TALER_PurseMergePrivateKeyP mpriv_in;
struct TALER_PurseMergePrivateKeyP mpriv_out;
json_t *c;
GNUNET_CRYPTO_ecdhe_key_create (&cpriv.ecdhe_priv);
GNUNET_CRYPTO_eddsa_key_create (&purse_priv.eddsa_priv);
GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv.eddsa_priv,
&purse_pub.eddsa_pub);
memset (&mpriv_in,
42,
sizeof (mpriv_in));
c = json_pack ("{s:s}", "test", "value");
GNUNET_assert (NULL != c);
TALER_CRYPTO_contract_encrypt_for_merge (&purse_pub,
&cpriv,
&mpriv_in,
c,
&econtract,
&econtract_size);
json_decref (c);
c = TALER_CRYPTO_contract_decrypt_for_merge (&cpriv,
&purse_pub,
econtract,
econtract_size,
&mpriv_out);
GNUNET_free (econtract);
if (NULL == c)
return 1;
json_decref (c);
if (0 != GNUNET_memcmp (&mpriv_in,
&mpriv_out))
return 1;
return 0;
}
int int
main (int argc, main (int argc,
const char *const argv[]) const char *const argv[])
@ -454,6 +498,8 @@ main (int argc,
return 4; return 4;
if (0 != test_merchant_sigs ()) if (0 != test_merchant_sigs ())
return 5; return 5;
if (0 != test_contracts ())
return 6;
return 0; return 0;
} }