wallet: t_s/d_us migration
This commit is contained in:
parent
32cd54e11d
commit
f8d12f7b0d
@ -1,402 +0,0 @@
|
|||||||
{
|
|
||||||
"history": [
|
|
||||||
{
|
|
||||||
"type": "exchange-added",
|
|
||||||
"builtIn": false,
|
|
||||||
"eventId": "exchange-added;https%3A%2F%2Fexchange.demo.taler.net%2F",
|
|
||||||
"exchangeBaseUrl": "https://exchange.demo.taler.net/",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334008633
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "exchange-updated",
|
|
||||||
"eventId": "exchange-updated;https%3A%2F%2Fexchange.demo.taler.net%2F",
|
|
||||||
"exchangeBaseUrl": "https://exchange.demo.taler.net/",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334009266
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "reserve-balance-updated",
|
|
||||||
"eventId": "reserve-balance-updated;HHG1KBFSW4PM8J43D14GVJYB8F5J56RDHANY1EQSW6RTYDAQJC6G",
|
|
||||||
"amountExpected": "KUDOS:5",
|
|
||||||
"amountReserveBalance": "KUDOS:5",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334039291
|
|
||||||
},
|
|
||||||
"newHistoryTransactions": [
|
|
||||||
{
|
|
||||||
"amount": "KUDOS:5",
|
|
||||||
"sender_account_url": "payto://x-taler-bank/bank.demo.taler.net/65",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334028000
|
|
||||||
},
|
|
||||||
"wire_reference": "000000000038Y",
|
|
||||||
"type": "DEPOSIT"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"reserveShortInfo": {
|
|
||||||
"exchangeBaseUrl": "https://exchange.demo.taler.net/",
|
|
||||||
"reserveCreationDetail": {
|
|
||||||
"type": "taler-bank-withdraw",
|
|
||||||
"bankUrl": "https://bank.demo.taler.net/api/withdraw-operation/6fd6a78f-3d12-4c91-b5e4-c6fc31f44e8d"
|
|
||||||
},
|
|
||||||
"reservePub": "JPE7VR8R985WQ7ZX3EEYRTEGJQ1FAFE7P3JK1J7WFJEP7AGNTJD0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "withdrawn",
|
|
||||||
"withdrawSessionId": "SFW3JS0JV0GZQQ1W07TNQEAGBD84X2QMH38PJ2CCTTKSDKQFCBY0",
|
|
||||||
"eventId": "withdrawn;SFW3JS0JV0GZQQ1W07TNQEAGBD84X2QMH38PJ2CCTTKSDKQFCBY0",
|
|
||||||
"amountWithdrawnEffective": "KUDOS:4.8",
|
|
||||||
"amountWithdrawnRaw": "KUDOS:5",
|
|
||||||
"exchangeBaseUrl": "https://exchange.demo.taler.net/",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334039853
|
|
||||||
},
|
|
||||||
"withdrawalSource": {
|
|
||||||
"type": "reserve",
|
|
||||||
"reservePub": "JPE7VR8R985WQ7ZX3EEYRTEGJQ1FAFE7P3JK1J7WFJEP7AGNTJD0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "order-accepted",
|
|
||||||
"eventId": "order-accepted;RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "KUDOS:0.5",
|
|
||||||
"orderId": "2020.006-G1NT65XRPQ8GP",
|
|
||||||
"merchantBaseUrl": "https://backend.demo.taler.net/public/instances/FSF/",
|
|
||||||
"proposalId": "RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
|
|
||||||
"summary": "Essay: 2. The GNU Project"
|
|
||||||
},
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334078823
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "order-redirected",
|
|
||||||
"eventId": "order-redirected;0W4EBHQJ90XX4TSQ9C0M6K9MBFJ1ENKTWH4R3CXFT986A2QHCESG",
|
|
||||||
"alreadyPaidOrderShortInfo": {
|
|
||||||
"amount": "KUDOS:0.5",
|
|
||||||
"orderId": "2020.006-G1NT65XRPQ8GP",
|
|
||||||
"merchantBaseUrl": "https://backend.demo.taler.net/public/instances/FSF/",
|
|
||||||
"proposalId": "RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
|
|
||||||
"summary": "Essay: 2. The GNU Project"
|
|
||||||
},
|
|
||||||
"newOrderShortInfo": {
|
|
||||||
"amount": "KUDOS:0.5",
|
|
||||||
"orderId": "2020.006-00W4ANVVKAHAP",
|
|
||||||
"merchantBaseUrl": "https://backend.demo.taler.net/public/instances/FSF/",
|
|
||||||
"proposalId": "0W4EBHQJ90XX4TSQ9C0M6K9MBFJ1ENKTWH4R3CXFT986A2QHCESG",
|
|
||||||
"summary": "Essay: 2. The GNU Project"
|
|
||||||
},
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334108380
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "payment-sent",
|
|
||||||
"eventId": "payment-sent;RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "KUDOS:0.5",
|
|
||||||
"orderId": "2020.006-G1NT65XRPQ8GP",
|
|
||||||
"merchantBaseUrl": "https://backend.demo.taler.net/public/instances/FSF/",
|
|
||||||
"proposalId": "RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
|
|
||||||
"summary": "Essay: 2. The GNU Project"
|
|
||||||
},
|
|
||||||
"replay": true,
|
|
||||||
"sessionId": "ab48396f-3aa1-4e1f-bfb5-30852d1e0d5e",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334108677
|
|
||||||
},
|
|
||||||
"numCoins": 6,
|
|
||||||
"amountPaidWithFees": "KUDOS:0.54"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "exchange-added",
|
|
||||||
"builtIn": false,
|
|
||||||
"eventId": "exchange-added;https%3A%2F%2Fexchange.test.taler.net%2F",
|
|
||||||
"exchangeBaseUrl": "https://exchange.test.taler.net/",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334134741
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "exchange-updated",
|
|
||||||
"eventId": "exchange-updated;https%3A%2F%2Fexchange.test.taler.net%2F",
|
|
||||||
"exchangeBaseUrl": "https://exchange.test.taler.net/",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334135451
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "reserve-balance-updated",
|
|
||||||
"eventId": "reserve-balance-updated;498DDH4ZB41QX45FH38T4Y8JM14WX8Q2J1VKKZTE0CMS6TCPYZAG",
|
|
||||||
"amountExpected": "TESTKUDOS:5",
|
|
||||||
"amountReserveBalance": "TESTKUDOS:5",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334141843
|
|
||||||
},
|
|
||||||
"newHistoryTransactions": [
|
|
||||||
{
|
|
||||||
"amount": "TESTKUDOS:5",
|
|
||||||
"sender_account_url": "payto://x-taler-bank/bank.test.taler.net/9",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334138000
|
|
||||||
},
|
|
||||||
"wire_reference": "0000000000184",
|
|
||||||
"type": "DEPOSIT"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"reserveShortInfo": {
|
|
||||||
"exchangeBaseUrl": "https://exchange.test.taler.net/",
|
|
||||||
"reserveCreationDetail": {
|
|
||||||
"type": "taler-bank-withdraw",
|
|
||||||
"bankUrl": "https://bank.test.taler.net/api/withdraw-operation/e6210f62-d27b-4f58-815c-c5160de8804c"
|
|
||||||
},
|
|
||||||
"reservePub": "ZQ2N7V8M035HAD1HTW7ZX22NM9GAXDCGX6GSJECD2KEY9TN3C0V0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "withdrawn",
|
|
||||||
"withdrawSessionId": "AAVX0GVZ8GRPYX2RWANQ9J279ABA7KNFYEQ3A0C63TW7NMV0GAT0",
|
|
||||||
"eventId": "withdrawn;AAVX0GVZ8GRPYX2RWANQ9J279ABA7KNFYEQ3A0C63TW7NMV0GAT0",
|
|
||||||
"amountWithdrawnEffective": "TESTKUDOS:5",
|
|
||||||
"amountWithdrawnRaw": "TESTKUDOS:5",
|
|
||||||
"exchangeBaseUrl": "https://exchange.test.taler.net/",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334142432
|
|
||||||
},
|
|
||||||
"withdrawalSource": {
|
|
||||||
"type": "reserve",
|
|
||||||
"reservePub": "ZQ2N7V8M035HAD1HTW7ZX22NM9GAXDCGX6GSJECD2KEY9TN3C0V0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "refreshed",
|
|
||||||
"refreshGroupId": "2TARBASBNCE0X7F0D89Z3TJGPXKRARFSBH3HKZ5JFQRKPV9CA5C0",
|
|
||||||
"eventId": "refreshed;2TARBASBNCE0X7F0D89Z3TJGPXKRARFSBH3HKZ5JFQRKPV9CA5C0",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334142528
|
|
||||||
},
|
|
||||||
"refreshReason": "pay",
|
|
||||||
"amountRefreshedEffective": "KUDOS:0",
|
|
||||||
"amountRefreshedRaw": "KUDOS:0.06",
|
|
||||||
"numInputCoins": 6,
|
|
||||||
"numOutputCoins": 0,
|
|
||||||
"numRefreshedInputCoins": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "order-accepted",
|
|
||||||
"eventId": "order-accepted;W39MQT31M1ZY3NPF9ZSGXM8Q1K5XS5D5R1J10ZSHBREC6EZ570F0",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "TESTKUDOS:1",
|
|
||||||
"orderId": "2020.006-00GBW7AD1VFRW",
|
|
||||||
"merchantBaseUrl": "https://backend.test.taler.net/public/instances/GNUnet/",
|
|
||||||
"proposalId": "W39MQT31M1ZY3NPF9ZSGXM8Q1K5XS5D5R1J10ZSHBREC6EZ570F0",
|
|
||||||
"summary": "Donation to GNUnet"
|
|
||||||
},
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334230099
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "payment-sent",
|
|
||||||
"eventId": "payment-sent;W39MQT31M1ZY3NPF9ZSGXM8Q1K5XS5D5R1J10ZSHBREC6EZ570F0",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "TESTKUDOS:1",
|
|
||||||
"orderId": "2020.006-00GBW7AD1VFRW",
|
|
||||||
"merchantBaseUrl": "https://backend.test.taler.net/public/instances/GNUnet/",
|
|
||||||
"proposalId": "W39MQT31M1ZY3NPF9ZSGXM8Q1K5XS5D5R1J10ZSHBREC6EZ570F0",
|
|
||||||
"summary": "Donation to GNUnet"
|
|
||||||
},
|
|
||||||
"replay": false,
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334232527
|
|
||||||
},
|
|
||||||
"numCoins": 4,
|
|
||||||
"amountPaidWithFees": "TESTKUDOS:1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "order-accepted",
|
|
||||||
"eventId": "order-accepted;Y8230SR6DP52J61CHEAPM5NHRVK408YP5KJP6VRBGZ3QZ0TBZQ90",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "TESTKUDOS:0.1",
|
|
||||||
"orderId": "2020.006-02RFGFSSAZY9Y",
|
|
||||||
"merchantBaseUrl": "https://backend.test.taler.net/public/instances/Taler/",
|
|
||||||
"proposalId": "Y8230SR6DP52J61CHEAPM5NHRVK408YP5KJP6VRBGZ3QZ0TBZQ90",
|
|
||||||
"summary": "Donation to Taler"
|
|
||||||
},
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334258703
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "payment-sent",
|
|
||||||
"eventId": "payment-sent;Y8230SR6DP52J61CHEAPM5NHRVK408YP5KJP6VRBGZ3QZ0TBZQ90",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "TESTKUDOS:0.1",
|
|
||||||
"orderId": "2020.006-02RFGFSSAZY9Y",
|
|
||||||
"merchantBaseUrl": "https://backend.test.taler.net/public/instances/Taler/",
|
|
||||||
"proposalId": "Y8230SR6DP52J61CHEAPM5NHRVK408YP5KJP6VRBGZ3QZ0TBZQ90",
|
|
||||||
"summary": "Donation to Taler"
|
|
||||||
},
|
|
||||||
"replay": false,
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334260497
|
|
||||||
},
|
|
||||||
"numCoins": 1,
|
|
||||||
"amountPaidWithFees": "TESTKUDOS:0.1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "reserve-balance-updated",
|
|
||||||
"eventId": "reserve-balance-updated;NBZX24YB4GEHFXFXD5NJAC84ZZD63DFAD6Q7YFJQGX8WX9YQ7B90",
|
|
||||||
"amountExpected": "TESTKUDOS:15",
|
|
||||||
"amountReserveBalance": "TESTKUDOS:15",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334530519
|
|
||||||
},
|
|
||||||
"newHistoryTransactions": [
|
|
||||||
{
|
|
||||||
"amount": "TESTKUDOS:15",
|
|
||||||
"sender_account_url": "payto://x-taler-bank/bank.test.taler.net/9",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334522000
|
|
||||||
},
|
|
||||||
"wire_reference": "000000000018C",
|
|
||||||
"type": "DEPOSIT"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"reserveShortInfo": {
|
|
||||||
"exchangeBaseUrl": "https://exchange.test.taler.net/",
|
|
||||||
"reserveCreationDetail": {
|
|
||||||
"type": "taler-bank-withdraw",
|
|
||||||
"bankUrl": "https://bank.test.taler.net/api/withdraw-operation/6b5fae55-3634-4e6b-a877-2f8bd76dfaf9"
|
|
||||||
},
|
|
||||||
"reservePub": "5XZC4DQMNR3482443727Q2RNKRVEBEW7SFJ8N9TYV5AZ74AZJB4G"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "withdrawn",
|
|
||||||
"withdrawSessionId": "312AKNY5BGF08PY1ZK0Z2QBEZ3481NFP9BN36Z08FHJQQZW80EZG",
|
|
||||||
"eventId": "withdrawn;312AKNY5BGF08PY1ZK0Z2QBEZ3481NFP9BN36Z08FHJQQZW80EZG",
|
|
||||||
"amountWithdrawnEffective": "TESTKUDOS:15",
|
|
||||||
"amountWithdrawnRaw": "TESTKUDOS:15",
|
|
||||||
"exchangeBaseUrl": "https://exchange.test.taler.net/",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334531085
|
|
||||||
},
|
|
||||||
"withdrawalSource": {
|
|
||||||
"type": "reserve",
|
|
||||||
"reservePub": "5XZC4DQMNR3482443727Q2RNKRVEBEW7SFJ8N9TYV5AZ74AZJB4G"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "refreshed",
|
|
||||||
"refreshGroupId": "3FN9PFD2JCPS3FDHZDPRS50VQT4G7SE54JDTG2ZW2HV1R3PJ9KBG",
|
|
||||||
"eventId": "refreshed;3FN9PFD2JCPS3FDHZDPRS50VQT4G7SE54JDTG2ZW2HV1R3PJ9KBG",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334532478
|
|
||||||
},
|
|
||||||
"refreshReason": "pay",
|
|
||||||
"amountRefreshedEffective": "TESTKUDOS:2.46",
|
|
||||||
"amountRefreshedRaw": "TESTKUDOS:2.46",
|
|
||||||
"numInputCoins": 1,
|
|
||||||
"numOutputCoins": 6,
|
|
||||||
"numRefreshedInputCoins": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "refreshed",
|
|
||||||
"refreshGroupId": "DF0DQ6MGJ39R0891B0P86MY2QTMPF9FPDJN30PRBMXBZ8XEVHZE0",
|
|
||||||
"eventId": "refreshed;DF0DQ6MGJ39R0891B0P86MY2QTMPF9FPDJN30PRBMXBZ8XEVHZE0",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334533177
|
|
||||||
},
|
|
||||||
"refreshReason": "pay",
|
|
||||||
"amountRefreshedEffective": "TESTKUDOS:1.12",
|
|
||||||
"amountRefreshedRaw": "TESTKUDOS:1.12",
|
|
||||||
"numInputCoins": 4,
|
|
||||||
"numOutputCoins": 3,
|
|
||||||
"numRefreshedInputCoins": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "order-accepted",
|
|
||||||
"eventId": "order-accepted;KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "TESTKUDOS:0.5",
|
|
||||||
"orderId": "2020.006-03WSPCDEZK6HG",
|
|
||||||
"merchantBaseUrl": "https://backend.test.taler.net/public/instances/FSF/",
|
|
||||||
"proposalId": "KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
|
|
||||||
"summary": "Essay: 6. Why Software Should Be Free"
|
|
||||||
},
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334554161
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "payment-sent",
|
|
||||||
"eventId": "payment-sent;KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "TESTKUDOS:0.5",
|
|
||||||
"orderId": "2020.006-03WSPCDEZK6HG",
|
|
||||||
"merchantBaseUrl": "https://backend.test.taler.net/public/instances/FSF/",
|
|
||||||
"proposalId": "KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
|
|
||||||
"summary": "Essay: 6. Why Software Should Be Free"
|
|
||||||
},
|
|
||||||
"replay": false,
|
|
||||||
"sessionId": "489c048b-7702-49e8-b66f-2de5e33f0008",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334556292
|
|
||||||
},
|
|
||||||
"numCoins": 5,
|
|
||||||
"amountPaidWithFees": "TESTKUDOS:0.5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "refreshed",
|
|
||||||
"refreshGroupId": "NG8Z05Q8CM7KCC98PDNDQR0G920J2SGYM15ACBV0PMBE6XA8Q87G",
|
|
||||||
"eventId": "refreshed;NG8Z05Q8CM7KCC98PDNDQR0G920J2SGYM15ACBV0PMBE6XA8Q87G",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334568850
|
|
||||||
},
|
|
||||||
"refreshReason": "pay",
|
|
||||||
"amountRefreshedEffective": "TESTKUDOS:0.06",
|
|
||||||
"amountRefreshedRaw": "TESTKUDOS:0.06",
|
|
||||||
"numInputCoins": 5,
|
|
||||||
"numOutputCoins": 2,
|
|
||||||
"numRefreshedInputCoins": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "refund",
|
|
||||||
"eventId": "refund;FRW9DGXKPFS9HZEGFYMABDF6FRXDYNMFHH23FT71AAKN8FHGV2EG",
|
|
||||||
"refundGroupId": "FRW9DGXKPFS9HZEGFYMABDF6FRXDYNMFHH23FT71AAKN8FHGV2EG",
|
|
||||||
"orderShortInfo": {
|
|
||||||
"amount": "TESTKUDOS:0.5",
|
|
||||||
"orderId": "2020.006-03WSPCDEZK6HG",
|
|
||||||
"merchantBaseUrl": "https://backend.test.taler.net/public/instances/FSF/",
|
|
||||||
"proposalId": "KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
|
|
||||||
"summary": "Essay: 6. Why Software Should Be Free"
|
|
||||||
},
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334581129
|
|
||||||
},
|
|
||||||
"amountRefundedEffective": "TESTKUDOS:0.5",
|
|
||||||
"amountRefundedRaw": "TESTKUDOS:0.5",
|
|
||||||
"amountRefundedInvalid": "TESTKUDOS:0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "refreshed",
|
|
||||||
"refreshGroupId": "TY8G0FDE83YJY3CWBYKMV891CADG06X8MTBZHE42XNQV2B2C95RG",
|
|
||||||
"eventId": "refreshed;TY8G0FDE83YJY3CWBYKMV891CADG06X8MTBZHE42XNQV2B2C95RG",
|
|
||||||
"timestamp": {
|
|
||||||
"t_ms": 1578334585583
|
|
||||||
},
|
|
||||||
"refreshReason": "refund",
|
|
||||||
"amountRefreshedEffective": "TESTKUDOS:0.5",
|
|
||||||
"amountRefreshedRaw": "TESTKUDOS:0.5",
|
|
||||||
"numInputCoins": 5,
|
|
||||||
"numOutputCoins": 6,
|
|
||||||
"numRefreshedInputCoins": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -19,8 +19,9 @@ import {
|
|||||||
parsePayUri,
|
parsePayUri,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
TalerSignaturePurpose,
|
TalerSignaturePurpose,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { anastasisData } from "./anastasis-data.js";
|
import { anastasisData } from "./anastasis-data.js";
|
||||||
import {
|
import {
|
||||||
@ -631,15 +632,13 @@ async function uploadSecret(
|
|||||||
logger.info(`got response for policy upload (http status ${resp.status})`);
|
logger.info(`got response for policy upload (http status ${resp.status})`);
|
||||||
if (resp.status === HttpStatusCode.NoContent) {
|
if (resp.status === HttpStatusCode.NoContent) {
|
||||||
let policyVersion = 0;
|
let policyVersion = 0;
|
||||||
let policyExpiration: Timestamp = { t_ms: 0 };
|
let policyExpiration: TalerProtocolTimestamp = { t_s: 0 };
|
||||||
try {
|
try {
|
||||||
policyVersion = Number(resp.headers.get("Anastasis-Version") ?? "0");
|
policyVersion = Number(resp.headers.get("Anastasis-Version") ?? "0");
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
try {
|
try {
|
||||||
policyExpiration = {
|
policyExpiration = {
|
||||||
t_ms:
|
t_s: Number(resp.headers.get("Anastasis-Policy-Expiration") ?? "0"),
|
||||||
1000 *
|
|
||||||
Number(resp.headers.get("Anastasis-Policy-Expiration") ?? "0"),
|
|
||||||
};
|
};
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
successDetails[prov.provider_url] = {
|
successDetails[prov.provider_url] = {
|
||||||
@ -1187,7 +1186,7 @@ async function addProviderBackup(
|
|||||||
state: ReducerStateBackup,
|
state: ReducerStateBackup,
|
||||||
args: ActionArgsAddProvider,
|
args: ActionArgsAddProvider,
|
||||||
): Promise<ReducerStateBackup> {
|
): Promise<ReducerStateBackup> {
|
||||||
const info = await getProviderInfo(args.provider_url)
|
const info = await getProviderInfo(args.provider_url);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
authentication_providers: {
|
authentication_providers: {
|
||||||
@ -1202,8 +1201,10 @@ async function deleteProviderBackup(
|
|||||||
state: ReducerStateBackup,
|
state: ReducerStateBackup,
|
||||||
args: ActionArgsDeleteProvider,
|
args: ActionArgsDeleteProvider,
|
||||||
): Promise<ReducerStateBackup> {
|
): Promise<ReducerStateBackup> {
|
||||||
const authentication_providers = {... state.authentication_providers ?? {} }
|
const authentication_providers = {
|
||||||
delete authentication_providers[args.provider_url]
|
...(state.authentication_providers ?? {}),
|
||||||
|
};
|
||||||
|
delete authentication_providers[args.provider_url];
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
authentication_providers,
|
authentication_providers,
|
||||||
@ -1214,7 +1215,7 @@ async function addProviderRecovery(
|
|||||||
state: ReducerStateRecovery,
|
state: ReducerStateRecovery,
|
||||||
args: ActionArgsAddProvider,
|
args: ActionArgsAddProvider,
|
||||||
): Promise<ReducerStateRecovery> {
|
): Promise<ReducerStateRecovery> {
|
||||||
const info = await getProviderInfo(args.provider_url)
|
const info = await getProviderInfo(args.provider_url);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
authentication_providers: {
|
authentication_providers: {
|
||||||
@ -1228,8 +1229,10 @@ async function deleteProviderRecovery(
|
|||||||
state: ReducerStateRecovery,
|
state: ReducerStateRecovery,
|
||||||
args: ActionArgsDeleteProvider,
|
args: ActionArgsDeleteProvider,
|
||||||
): Promise<ReducerStateRecovery> {
|
): Promise<ReducerStateRecovery> {
|
||||||
const authentication_providers = {... state.authentication_providers ?? {} }
|
const authentication_providers = {
|
||||||
delete authentication_providers[args.provider_url]
|
...(state.authentication_providers ?? {}),
|
||||||
|
};
|
||||||
|
delete authentication_providers[args.provider_url];
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
authentication_providers,
|
authentication_providers,
|
||||||
@ -1347,7 +1350,8 @@ async function updateUploadFees(
|
|||||||
x,
|
x,
|
||||||
).amount;
|
).amount;
|
||||||
};
|
};
|
||||||
const years = Duration.toIntegerYears(Duration.getRemaining(expiration));
|
const expirationTime = AbsoluteTime.fromTimestamp(expiration);
|
||||||
|
const years = Duration.toIntegerYears(Duration.getRemaining(expirationTime));
|
||||||
logger.info(`computing fees for ${years} years`);
|
logger.info(`computing fees for ${years} years`);
|
||||||
// For now, we compute fees for *all* available providers.
|
// For now, we compute fees for *all* available providers.
|
||||||
for (const provUrl in state.authentication_providers ?? {}) {
|
for (const provUrl in state.authentication_providers ?? {}) {
|
||||||
|
@ -7,7 +7,8 @@ import {
|
|||||||
codecForString,
|
codecForString,
|
||||||
codecForTimestamp,
|
codecForTimestamp,
|
||||||
Duration,
|
Duration,
|
||||||
Timestamp,
|
TalerProtocolTimestamp,
|
||||||
|
AbsoluteTime,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { ChallengeFeedback } from "./challenge-feedback-types.js";
|
import { ChallengeFeedback } from "./challenge-feedback-types.js";
|
||||||
import { KeyShare } from "./crypto.js";
|
import { KeyShare } from "./crypto.js";
|
||||||
@ -43,7 +44,7 @@ export interface PolicyProvider {
|
|||||||
export interface SuccessDetails {
|
export interface SuccessDetails {
|
||||||
[provider_url: string]: {
|
[provider_url: string]: {
|
||||||
policy_version: number;
|
policy_version: number;
|
||||||
policy_expiration: Timestamp;
|
policy_expiration: TalerProtocolTimestamp;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +113,7 @@ export interface ReducerStateBackup {
|
|||||||
|
|
||||||
core_secret?: CoreSecret;
|
core_secret?: CoreSecret;
|
||||||
|
|
||||||
expiration?: Timestamp;
|
expiration?: TalerProtocolTimestamp;
|
||||||
|
|
||||||
upload_fees?: { fee: AmountString }[];
|
upload_fees?: { fee: AmountString }[];
|
||||||
|
|
||||||
@ -369,7 +370,7 @@ export interface ActionArgsEnterSecret {
|
|||||||
value: string;
|
value: string;
|
||||||
mime?: string;
|
mime?: string;
|
||||||
};
|
};
|
||||||
expiration: Timestamp;
|
expiration: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActionArgsSelectContinent {
|
export interface ActionArgsSelectContinent {
|
||||||
@ -438,7 +439,7 @@ export interface ActionArgsAddPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ActionArgsUpdateExpiration {
|
export interface ActionArgsUpdateExpiration {
|
||||||
expiration: Timestamp;
|
expiration: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActionArgsChangeVersion {
|
export interface ActionArgsChangeVersion {
|
||||||
|
@ -51,13 +51,13 @@ export const WithDetails = createExample(TestedComponent, {
|
|||||||
success_details: {
|
success_details: {
|
||||||
"https://anastasis.demo.taler.net/": {
|
"https://anastasis.demo.taler.net/": {
|
||||||
policy_expiration: {
|
policy_expiration: {
|
||||||
t_ms: "never",
|
t_s: "never",
|
||||||
},
|
},
|
||||||
policy_version: 0,
|
policy_version: 0,
|
||||||
},
|
},
|
||||||
"https://kudos.demo.anastasis.lu/": {
|
"https://kudos.demo.anastasis.lu/": {
|
||||||
policy_expiration: {
|
policy_expiration: {
|
||||||
t_ms: new Date().getTime() + 60 * 60 * 24 * 1000,
|
t_s: new Date().getTime() + 60 * 60 * 24,
|
||||||
},
|
},
|
||||||
policy_version: 1,
|
policy_version: 1,
|
||||||
},
|
},
|
||||||
|
@ -35,9 +35,9 @@ export function BackupFinishedScreen(): VNode {
|
|||||||
</a>
|
</a>
|
||||||
<p>
|
<p>
|
||||||
version {sd.policy_version}
|
version {sd.policy_version}
|
||||||
{sd.policy_expiration.t_ms !== "never"
|
{sd.policy_expiration.t_s !== "never"
|
||||||
? ` expires at: ${format(
|
? ` expires at: ${format(
|
||||||
new Date(sd.policy_expiration.t_ms),
|
new Date(sd.policy_expiration.t_s),
|
||||||
"dd-MM-yyyy",
|
"dd-MM-yyyy",
|
||||||
)}`
|
)}`
|
||||||
: " without expiration date"}
|
: " without expiration date"}
|
||||||
|
@ -52,7 +52,7 @@ export function SecretEditorScreen(): VNode {
|
|||||||
await tx.transition("enter_secret", {
|
await tx.transition("enter_secret", {
|
||||||
secret,
|
secret,
|
||||||
expiration: {
|
expiration: {
|
||||||
t_ms: new Date().getTime() + 1000 * 60 * 60 * 24 * 365 * 5,
|
t_s: new Date().getTime() + 60 * 60 * 24 * 365 * 5,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await tx.transition("next", {});
|
await tx.transition("next", {});
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Logger } from "./logging.js";
|
import { Logger } from "./logging.js";
|
||||||
import { getTimestampNow, timestampCmp, timestampDifference } from "./time.js";
|
import { AbsoluteTime } from "./time.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of token bucket throttling.
|
* Implementation of token bucket throttling.
|
||||||
@ -46,16 +46,16 @@ class OriginState {
|
|||||||
tokensSecond: number = MAX_PER_SECOND;
|
tokensSecond: number = MAX_PER_SECOND;
|
||||||
tokensMinute: number = MAX_PER_MINUTE;
|
tokensMinute: number = MAX_PER_MINUTE;
|
||||||
tokensHour: number = MAX_PER_HOUR;
|
tokensHour: number = MAX_PER_HOUR;
|
||||||
private lastUpdate = getTimestampNow();
|
private lastUpdate = AbsoluteTime.now();
|
||||||
|
|
||||||
private refill(): void {
|
private refill(): void {
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.now();
|
||||||
if (timestampCmp(now, this.lastUpdate) < 0) {
|
if (AbsoluteTime.cmp(now, this.lastUpdate) < 0) {
|
||||||
// Did the system time change?
|
// Did the system time change?
|
||||||
this.lastUpdate = now;
|
this.lastUpdate = now;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const d = timestampDifference(now, this.lastUpdate);
|
const d = AbsoluteTime.difference(now, this.lastUpdate);
|
||||||
if (d.d_ms === "forever") {
|
if (d.d_ms === "forever") {
|
||||||
throw Error("assertion failed");
|
throw Error("assertion failed");
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,11 @@ import {
|
|||||||
EddsaPublicKeyString,
|
EddsaPublicKeyString,
|
||||||
CoinPublicKeyString,
|
CoinPublicKeyString,
|
||||||
} from "./talerTypes";
|
} from "./talerTypes";
|
||||||
import { Timestamp, codecForTimestamp } from "./time.js";
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
|
codecForTimestamp,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
|
} from "./time.js";
|
||||||
|
|
||||||
export enum ReserveTransactionType {
|
export enum ReserveTransactionType {
|
||||||
Withdraw = "WITHDRAW",
|
Withdraw = "WITHDRAW",
|
||||||
@ -98,7 +102,7 @@ export interface ReserveCreditTransaction {
|
|||||||
/**
|
/**
|
||||||
* Timestamp of the incoming wire transfer.
|
* Timestamp of the incoming wire transfer.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReserveClosingTransaction {
|
export interface ReserveClosingTransaction {
|
||||||
@ -139,7 +143,7 @@ export interface ReserveClosingTransaction {
|
|||||||
/**
|
/**
|
||||||
* Time when the reserve was closed.
|
* Time when the reserve was closed.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReserveRecoupTransaction {
|
export interface ReserveRecoupTransaction {
|
||||||
@ -165,7 +169,7 @@ export interface ReserveRecoupTransaction {
|
|||||||
/**
|
/**
|
||||||
* Time when the funds were paid back into the reserve.
|
* Time when the funds were paid back into the reserve.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public key of the coin that was paid back.
|
* Public key of the coin that was paid back.
|
||||||
@ -182,46 +186,50 @@ export type ReserveTransaction =
|
|||||||
| ReserveClosingTransaction
|
| ReserveClosingTransaction
|
||||||
| ReserveRecoupTransaction;
|
| ReserveRecoupTransaction;
|
||||||
|
|
||||||
export const codecForReserveWithdrawTransaction = (): Codec<ReserveWithdrawTransaction> =>
|
export const codecForReserveWithdrawTransaction =
|
||||||
buildCodecForObject<ReserveWithdrawTransaction>()
|
(): Codec<ReserveWithdrawTransaction> =>
|
||||||
.property("amount", codecForString())
|
buildCodecForObject<ReserveWithdrawTransaction>()
|
||||||
.property("h_coin_envelope", codecForString())
|
.property("amount", codecForString())
|
||||||
.property("h_denom_pub", codecForString())
|
.property("h_coin_envelope", codecForString())
|
||||||
.property("reserve_sig", codecForString())
|
.property("h_denom_pub", codecForString())
|
||||||
.property("type", codecForConstString(ReserveTransactionType.Withdraw))
|
.property("reserve_sig", codecForString())
|
||||||
.property("withdraw_fee", codecForString())
|
.property("type", codecForConstString(ReserveTransactionType.Withdraw))
|
||||||
.build("ReserveWithdrawTransaction");
|
.property("withdraw_fee", codecForString())
|
||||||
|
.build("ReserveWithdrawTransaction");
|
||||||
|
|
||||||
export const codecForReserveCreditTransaction = (): Codec<ReserveCreditTransaction> =>
|
export const codecForReserveCreditTransaction =
|
||||||
buildCodecForObject<ReserveCreditTransaction>()
|
(): Codec<ReserveCreditTransaction> =>
|
||||||
.property("amount", codecForString())
|
buildCodecForObject<ReserveCreditTransaction>()
|
||||||
.property("sender_account_url", codecForString())
|
.property("amount", codecForString())
|
||||||
.property("timestamp", codecForTimestamp)
|
.property("sender_account_url", codecForString())
|
||||||
.property("wire_reference", codecForNumber())
|
.property("timestamp", codecForTimestamp)
|
||||||
.property("type", codecForConstString(ReserveTransactionType.Credit))
|
.property("wire_reference", codecForNumber())
|
||||||
.build("ReserveCreditTransaction");
|
.property("type", codecForConstString(ReserveTransactionType.Credit))
|
||||||
|
.build("ReserveCreditTransaction");
|
||||||
|
|
||||||
export const codecForReserveClosingTransaction = (): Codec<ReserveClosingTransaction> =>
|
export const codecForReserveClosingTransaction =
|
||||||
buildCodecForObject<ReserveClosingTransaction>()
|
(): Codec<ReserveClosingTransaction> =>
|
||||||
.property("amount", codecForString())
|
buildCodecForObject<ReserveClosingTransaction>()
|
||||||
.property("closing_fee", codecForString())
|
.property("amount", codecForString())
|
||||||
.property("exchange_pub", codecForString())
|
.property("closing_fee", codecForString())
|
||||||
.property("exchange_sig", codecForString())
|
.property("exchange_pub", codecForString())
|
||||||
.property("h_wire", codecForString())
|
.property("exchange_sig", codecForString())
|
||||||
.property("timestamp", codecForTimestamp)
|
.property("h_wire", codecForString())
|
||||||
.property("type", codecForConstString(ReserveTransactionType.Closing))
|
.property("timestamp", codecForTimestamp)
|
||||||
.property("wtid", codecForString())
|
.property("type", codecForConstString(ReserveTransactionType.Closing))
|
||||||
.build("ReserveClosingTransaction");
|
.property("wtid", codecForString())
|
||||||
|
.build("ReserveClosingTransaction");
|
||||||
|
|
||||||
export const codecForReserveRecoupTransaction = (): Codec<ReserveRecoupTransaction> =>
|
export const codecForReserveRecoupTransaction =
|
||||||
buildCodecForObject<ReserveRecoupTransaction>()
|
(): Codec<ReserveRecoupTransaction> =>
|
||||||
.property("amount", codecForString())
|
buildCodecForObject<ReserveRecoupTransaction>()
|
||||||
.property("coin_pub", codecForString())
|
.property("amount", codecForString())
|
||||||
.property("exchange_pub", codecForString())
|
.property("coin_pub", codecForString())
|
||||||
.property("exchange_sig", codecForString())
|
.property("exchange_pub", codecForString())
|
||||||
.property("timestamp", codecForTimestamp)
|
.property("exchange_sig", codecForString())
|
||||||
.property("type", codecForConstString(ReserveTransactionType.Recoup))
|
.property("timestamp", codecForTimestamp)
|
||||||
.build("ReserveRecoupTransaction");
|
.property("type", codecForConstString(ReserveTransactionType.Recoup))
|
||||||
|
.build("ReserveRecoupTransaction");
|
||||||
|
|
||||||
export const codecForReserveTransaction = (): Codec<ReserveTransaction> =>
|
export const codecForReserveTransaction = (): Codec<ReserveTransaction> =>
|
||||||
buildCodecForUnion<ReserveTransaction>()
|
buildCodecForUnion<ReserveTransaction>()
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { DenominationPubKey, UnblindedSignature } from "./talerTypes.js";
|
import { DenominationPubKey, UnblindedSignature } from "./talerTypes.js";
|
||||||
import { Duration, Timestamp } from "./time.js";
|
import { TalerProtocolDuration, TalerProtocolTimestamp } from "./time.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type alias for strings that are to be treated like amounts.
|
* Type alias for strings that are to be treated like amounts.
|
||||||
@ -120,7 +120,7 @@ export interface WalletBackupContentV1 {
|
|||||||
* This timestamp should only be advanced if the content
|
* This timestamp should only be advanced if the content
|
||||||
* of the backup changes.
|
* of the backup changes.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per-exchange data sorted by exchange master public key.
|
* Per-exchange data sorted by exchange master public key.
|
||||||
@ -333,10 +333,10 @@ export interface BackupRecoupGroup {
|
|||||||
/**
|
/**
|
||||||
* Timestamp when the recoup was started.
|
* Timestamp when the recoup was started.
|
||||||
*/
|
*/
|
||||||
timestamp_created: Timestamp;
|
timestamp_created: TalerProtocolTimestamp;
|
||||||
|
|
||||||
timestamp_finish?: Timestamp;
|
timestamp_finish?: TalerProtocolTimestamp;
|
||||||
finish_clock?: Timestamp;
|
finish_clock?: TalerProtocolTimestamp;
|
||||||
finish_is_failure?: boolean;
|
finish_is_failure?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -486,14 +486,14 @@ export interface BackupTip {
|
|||||||
* Has the user accepted the tip? Only after the tip has been accepted coins
|
* Has the user accepted the tip? Only after the tip has been accepted coins
|
||||||
* withdrawn from the tip may be used.
|
* withdrawn from the tip may be used.
|
||||||
*/
|
*/
|
||||||
timestamp_accepted: Timestamp | undefined;
|
timestamp_accepted: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When was the tip first scanned by the wallet?
|
* When was the tip first scanned by the wallet?
|
||||||
*/
|
*/
|
||||||
timestamp_created: Timestamp;
|
timestamp_created: TalerProtocolTimestamp;
|
||||||
|
|
||||||
timestamp_finished?: Timestamp;
|
timestamp_finished?: TalerProtocolTimestamp;
|
||||||
finish_is_failure?: boolean;
|
finish_is_failure?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -504,7 +504,7 @@ export interface BackupTip {
|
|||||||
/**
|
/**
|
||||||
* Timestamp, the tip can't be picked up anymore after this deadline.
|
* Timestamp, the tip can't be picked up anymore after this deadline.
|
||||||
*/
|
*/
|
||||||
timestamp_expiration: Timestamp;
|
timestamp_expiration: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The exchange that will sign our coins, chosen by the merchant.
|
* The exchange that will sign our coins, chosen by the merchant.
|
||||||
@ -613,9 +613,9 @@ export interface BackupRefreshGroup {
|
|||||||
*/
|
*/
|
||||||
old_coins: BackupRefreshOldCoin[];
|
old_coins: BackupRefreshOldCoin[];
|
||||||
|
|
||||||
timestamp_created: Timestamp;
|
timestamp_created: TalerProtocolTimestamp;
|
||||||
|
|
||||||
timestamp_finish?: Timestamp;
|
timestamp_finish?: TalerProtocolTimestamp;
|
||||||
finish_is_failure?: boolean;
|
finish_is_failure?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,9 +636,9 @@ export interface BackupWithdrawalGroup {
|
|||||||
* When was the withdrawal operation started started?
|
* When was the withdrawal operation started started?
|
||||||
* Timestamp in milliseconds.
|
* Timestamp in milliseconds.
|
||||||
*/
|
*/
|
||||||
timestamp_created: Timestamp;
|
timestamp_created: TalerProtocolTimestamp;
|
||||||
|
|
||||||
timestamp_finish?: Timestamp;
|
timestamp_finish?: TalerProtocolTimestamp;
|
||||||
finish_is_failure?: boolean;
|
finish_is_failure?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -672,12 +672,12 @@ export interface BackupRefundItemCommon {
|
|||||||
/**
|
/**
|
||||||
* Execution time as claimed by the merchant
|
* Execution time as claimed by the merchant
|
||||||
*/
|
*/
|
||||||
execution_time: Timestamp;
|
execution_time: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the wallet became aware of the refund.
|
* Time when the wallet became aware of the refund.
|
||||||
*/
|
*/
|
||||||
obtained_time: Timestamp;
|
obtained_time: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount refunded for the coin.
|
* Amount refunded for the coin.
|
||||||
@ -788,7 +788,7 @@ export interface BackupPurchase {
|
|||||||
* Timestamp of the first time that sending a payment to the merchant
|
* Timestamp of the first time that sending a payment to the merchant
|
||||||
* for this purchase was successful.
|
* for this purchase was successful.
|
||||||
*/
|
*/
|
||||||
timestamp_first_successful_pay: Timestamp | undefined;
|
timestamp_first_successful_pay: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature by the merchant confirming the payment.
|
* Signature by the merchant confirming the payment.
|
||||||
@ -799,7 +799,7 @@ export interface BackupPurchase {
|
|||||||
* When was the purchase made?
|
* When was the purchase made?
|
||||||
* Refers to the time that the user accepted.
|
* Refers to the time that the user accepted.
|
||||||
*/
|
*/
|
||||||
timestamp_accept: Timestamp;
|
timestamp_accept: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pending refunds for the purchase. A refund is pending
|
* Pending refunds for the purchase. A refund is pending
|
||||||
@ -815,7 +815,7 @@ export interface BackupPurchase {
|
|||||||
/**
|
/**
|
||||||
* Continue querying the refund status until this deadline has expired.
|
* Continue querying the refund status until this deadline has expired.
|
||||||
*/
|
*/
|
||||||
auto_refund_deadline: Timestamp | undefined;
|
auto_refund_deadline: TalerProtocolTimestamp | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -857,22 +857,22 @@ export interface BackupDenomination {
|
|||||||
/**
|
/**
|
||||||
* Validity start date of the denomination.
|
* Validity start date of the denomination.
|
||||||
*/
|
*/
|
||||||
stamp_start: Timestamp;
|
stamp_start: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date after which the currency can't be withdrawn anymore.
|
* Date after which the currency can't be withdrawn anymore.
|
||||||
*/
|
*/
|
||||||
stamp_expire_withdraw: Timestamp;
|
stamp_expire_withdraw: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date after the denomination officially doesn't exist anymore.
|
* Date after the denomination officially doesn't exist anymore.
|
||||||
*/
|
*/
|
||||||
stamp_expire_legal: Timestamp;
|
stamp_expire_legal: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data after which coins of this denomination can't be deposited anymore.
|
* Data after which coins of this denomination can't be deposited anymore.
|
||||||
*/
|
*/
|
||||||
stamp_expire_deposit: Timestamp;
|
stamp_expire_deposit: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature by the exchange's master key over the denomination
|
* Signature by the exchange's master key over the denomination
|
||||||
@ -903,7 +903,7 @@ export interface BackupDenomination {
|
|||||||
* The list issue date of the exchange "/keys" response
|
* The list issue date of the exchange "/keys" response
|
||||||
* that this denomination was last seen in.
|
* that this denomination was last seen in.
|
||||||
*/
|
*/
|
||||||
list_issue_date: Timestamp;
|
list_issue_date: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -923,14 +923,14 @@ export interface BackupReserve {
|
|||||||
/**
|
/**
|
||||||
* Time when the reserve was created.
|
* Time when the reserve was created.
|
||||||
*/
|
*/
|
||||||
timestamp_created: Timestamp;
|
timestamp_created: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp of the last observed activity.
|
* Timestamp of the last observed activity.
|
||||||
*
|
*
|
||||||
* Used to compute when to give up querying the exchange.
|
* Used to compute when to give up querying the exchange.
|
||||||
*/
|
*/
|
||||||
timestamp_last_activity: Timestamp;
|
timestamp_last_activity: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp of when the reserve closed.
|
* Timestamp of when the reserve closed.
|
||||||
@ -938,7 +938,7 @@ export interface BackupReserve {
|
|||||||
* Note that the last activity can be after the closing time
|
* Note that the last activity can be after the closing time
|
||||||
* due to recouping.
|
* due to recouping.
|
||||||
*/
|
*/
|
||||||
timestamp_closed?: Timestamp;
|
timestamp_closed?: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire information (as payto URI) for the bank account that
|
* Wire information (as payto URI) for the bank account that
|
||||||
@ -977,14 +977,14 @@ export interface BackupReserve {
|
|||||||
/**
|
/**
|
||||||
* Time when the information about this reserve was posted to the bank.
|
* Time when the information about this reserve was posted to the bank.
|
||||||
*/
|
*/
|
||||||
timestamp_reserve_info_posted: Timestamp | undefined;
|
timestamp_reserve_info_posted: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the reserve was confirmed by the bank.
|
* Time when the reserve was confirmed by the bank.
|
||||||
*
|
*
|
||||||
* Set to undefined if not confirmed yet.
|
* Set to undefined if not confirmed yet.
|
||||||
*/
|
*/
|
||||||
timestamp_bank_confirmed: Timestamp | undefined;
|
timestamp_bank_confirmed: TalerProtocolTimestamp | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1033,12 +1033,12 @@ export interface BackupExchangeWireFee {
|
|||||||
/**
|
/**
|
||||||
* Start date of the fee.
|
* Start date of the fee.
|
||||||
*/
|
*/
|
||||||
start_stamp: Timestamp;
|
start_stamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End date of the fee.
|
* End date of the fee.
|
||||||
*/
|
*/
|
||||||
end_stamp: Timestamp;
|
end_stamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature made by the exchange master key.
|
* Signature made by the exchange master key.
|
||||||
@ -1050,9 +1050,9 @@ export interface BackupExchangeWireFee {
|
|||||||
* Structure of one exchange signing key in the /keys response.
|
* Structure of one exchange signing key in the /keys response.
|
||||||
*/
|
*/
|
||||||
export class BackupExchangeSignKey {
|
export class BackupExchangeSignKey {
|
||||||
stamp_start: Timestamp;
|
stamp_start: TalerProtocolTimestamp;
|
||||||
stamp_expire: Timestamp;
|
stamp_expire: TalerProtocolTimestamp;
|
||||||
stamp_end: Timestamp;
|
stamp_end: TalerProtocolTimestamp;
|
||||||
key: string;
|
key: string;
|
||||||
master_sig: string;
|
master_sig: string;
|
||||||
}
|
}
|
||||||
@ -1112,7 +1112,7 @@ export interface BackupExchange {
|
|||||||
*
|
*
|
||||||
* Used to facilitate automatic merging.
|
* Used to facilitate automatic merging.
|
||||||
*/
|
*/
|
||||||
update_clock: Timestamp;
|
update_clock: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1161,7 +1161,7 @@ export interface BackupExchangeDetails {
|
|||||||
/**
|
/**
|
||||||
* Closing delay of reserves.
|
* Closing delay of reserves.
|
||||||
*/
|
*/
|
||||||
reserve_closing_delay: Duration;
|
reserve_closing_delay: TalerProtocolDuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signing keys we got from the exchange, can also contain
|
* Signing keys we got from the exchange, can also contain
|
||||||
@ -1187,7 +1187,7 @@ export interface BackupExchangeDetails {
|
|||||||
/**
|
/**
|
||||||
* Timestamp when the ToS has been accepted.
|
* Timestamp when the ToS has been accepted.
|
||||||
*/
|
*/
|
||||||
tos_accepted_timestamp: Timestamp | undefined;
|
tos_accepted_timestamp: TalerProtocolTimestamp | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BackupProposalStatus {
|
export enum BackupProposalStatus {
|
||||||
@ -1248,7 +1248,7 @@ export interface BackupProposal {
|
|||||||
* Timestamp of when the record
|
* Timestamp of when the record
|
||||||
* was created.
|
* was created.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private key for the nonce.
|
* Private key for the nonce.
|
||||||
|
@ -38,15 +38,14 @@ import {
|
|||||||
codecForConstNumber,
|
codecForConstNumber,
|
||||||
buildCodecForUnion,
|
buildCodecForUnion,
|
||||||
codecForConstString,
|
codecForConstString,
|
||||||
codecForEither,
|
|
||||||
} from "./codec.js";
|
} from "./codec.js";
|
||||||
import {
|
import {
|
||||||
Timestamp,
|
|
||||||
codecForTimestamp,
|
codecForTimestamp,
|
||||||
Duration,
|
|
||||||
codecForDuration,
|
codecForDuration,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
|
TalerProtocolDuration,
|
||||||
} from "./time.js";
|
} from "./time.js";
|
||||||
import { Amounts, codecForAmountString } from "./amounts.js";
|
import { codecForAmountString } from "./amounts.js";
|
||||||
import { strcmp } from "./helpers.js";
|
import { strcmp } from "./helpers.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,24 +85,24 @@ export class ExchangeDenomination {
|
|||||||
/**
|
/**
|
||||||
* Start date from which withdraw is allowed.
|
* Start date from which withdraw is allowed.
|
||||||
*/
|
*/
|
||||||
stamp_start: Timestamp;
|
stamp_start: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End date for withdrawing.
|
* End date for withdrawing.
|
||||||
*/
|
*/
|
||||||
stamp_expire_withdraw: Timestamp;
|
stamp_expire_withdraw: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expiration date after which the exchange can forget about
|
* Expiration date after which the exchange can forget about
|
||||||
* the currency.
|
* the currency.
|
||||||
*/
|
*/
|
||||||
stamp_expire_legal: Timestamp;
|
stamp_expire_legal: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date after which the coins of this denomination can't be
|
* Date after which the coins of this denomination can't be
|
||||||
* deposited anymore.
|
* deposited anymore.
|
||||||
*/
|
*/
|
||||||
stamp_expire_deposit: Timestamp;
|
stamp_expire_deposit: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature over the denomination information by the exchange's master
|
* Signature over the denomination information by the exchange's master
|
||||||
@ -394,7 +393,7 @@ export interface Product {
|
|||||||
taxes?: Tax[];
|
taxes?: Tax[];
|
||||||
|
|
||||||
// time indicating when this product should be delivered
|
// time indicating when this product should be delivered
|
||||||
delivery_date?: Timestamp;
|
delivery_date?: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InternationalizedString {
|
export interface InternationalizedString {
|
||||||
@ -413,7 +412,7 @@ export interface ContractTerms {
|
|||||||
/**
|
/**
|
||||||
* Hash of the merchant's wire details.
|
* Hash of the merchant's wire details.
|
||||||
*/
|
*/
|
||||||
auto_refund?: Duration;
|
auto_refund?: TalerProtocolDuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire method the merchant wants to use.
|
* Wire method the merchant wants to use.
|
||||||
@ -445,7 +444,7 @@ export interface ContractTerms {
|
|||||||
/**
|
/**
|
||||||
* Deadline to pay for the contract.
|
* Deadline to pay for the contract.
|
||||||
*/
|
*/
|
||||||
pay_deadline: Timestamp;
|
pay_deadline: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum deposit fee covered by the merchant.
|
* Maximum deposit fee covered by the merchant.
|
||||||
@ -466,7 +465,7 @@ export interface ContractTerms {
|
|||||||
* Time indicating when the order should be delivered.
|
* Time indicating when the order should be delivered.
|
||||||
* May be overwritten by individual products.
|
* May be overwritten by individual products.
|
||||||
*/
|
*/
|
||||||
delivery_date?: Timestamp;
|
delivery_date?: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delivery location for (all!) products.
|
* Delivery location for (all!) products.
|
||||||
@ -486,17 +485,17 @@ export interface ContractTerms {
|
|||||||
/**
|
/**
|
||||||
* Deadline for refunds.
|
* Deadline for refunds.
|
||||||
*/
|
*/
|
||||||
refund_deadline: Timestamp;
|
refund_deadline: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deadline for the wire transfer.
|
* Deadline for the wire transfer.
|
||||||
*/
|
*/
|
||||||
wire_transfer_deadline: Timestamp;
|
wire_transfer_deadline: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the contract was generated by the merchant.
|
* Time when the contract was generated by the merchant.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Order id to uniquely identify the purchase within
|
* Order id to uniquely identify the purchase within
|
||||||
@ -700,9 +699,9 @@ export class Recoup {
|
|||||||
* Structure of one exchange signing key in the /keys response.
|
* Structure of one exchange signing key in the /keys response.
|
||||||
*/
|
*/
|
||||||
export class ExchangeSignKeyJson {
|
export class ExchangeSignKeyJson {
|
||||||
stamp_start: Timestamp;
|
stamp_start: TalerProtocolTimestamp;
|
||||||
stamp_expire: Timestamp;
|
stamp_expire: TalerProtocolTimestamp;
|
||||||
stamp_end: Timestamp;
|
stamp_end: TalerProtocolTimestamp;
|
||||||
key: EddsaPublicKeyString;
|
key: EddsaPublicKeyString;
|
||||||
master_sig: EddsaSignatureString;
|
master_sig: EddsaSignatureString;
|
||||||
}
|
}
|
||||||
@ -729,7 +728,7 @@ export class ExchangeKeysJson {
|
|||||||
/**
|
/**
|
||||||
* Timestamp when this response was issued.
|
* Timestamp when this response was issued.
|
||||||
*/
|
*/
|
||||||
list_issue_date: Timestamp;
|
list_issue_date: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of revoked denominations.
|
* List of revoked denominations.
|
||||||
@ -747,7 +746,7 @@ export class ExchangeKeysJson {
|
|||||||
*/
|
*/
|
||||||
version: string;
|
version: string;
|
||||||
|
|
||||||
reserve_closing_delay: Duration;
|
reserve_closing_delay: TalerProtocolDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -774,12 +773,12 @@ export class WireFeesJson {
|
|||||||
/**
|
/**
|
||||||
* Date from which the fee applies.
|
* Date from which the fee applies.
|
||||||
*/
|
*/
|
||||||
start_date: Timestamp;
|
start_date: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data after which the fee doesn't apply anymore.
|
* Data after which the fee doesn't apply anymore.
|
||||||
*/
|
*/
|
||||||
end_date: Timestamp;
|
end_date: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountInfo {
|
export interface AccountInfo {
|
||||||
@ -850,7 +849,7 @@ export class TipPickupGetResponse {
|
|||||||
|
|
||||||
exchange_url: string;
|
exchange_url: string;
|
||||||
|
|
||||||
expiration: Timestamp;
|
expiration: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum DenomKeyType {
|
export enum DenomKeyType {
|
||||||
@ -1067,7 +1066,7 @@ export interface MerchantCoinRefundSuccessStatus {
|
|||||||
// to the customer.
|
// to the customer.
|
||||||
refund_amount: AmountString;
|
refund_amount: AmountString;
|
||||||
|
|
||||||
execution_time: Timestamp;
|
execution_time: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MerchantCoinRefundFailureStatus {
|
export interface MerchantCoinRefundFailureStatus {
|
||||||
@ -1092,7 +1091,7 @@ export interface MerchantCoinRefundFailureStatus {
|
|||||||
// to the customer.
|
// to the customer.
|
||||||
refund_amount: AmountString;
|
refund_amount: AmountString;
|
||||||
|
|
||||||
execution_time: Timestamp;
|
execution_time: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MerchantOrderStatusUnpaid {
|
export interface MerchantOrderStatusUnpaid {
|
||||||
@ -1733,7 +1732,7 @@ export interface DepositSuccess {
|
|||||||
transaction_base_url?: string;
|
transaction_base_url?: string;
|
||||||
|
|
||||||
// timestamp when the deposit was received by the exchange.
|
// timestamp when the deposit was received by the exchange.
|
||||||
exchange_timestamp: Timestamp;
|
exchange_timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
// the EdDSA signature of TALER_DepositConfirmationPS using a current
|
// the EdDSA signature of TALER_DepositConfirmationPS using a current
|
||||||
// signing key of the exchange affirming the successful
|
// signing key of the exchange affirming the successful
|
||||||
|
@ -23,13 +23,41 @@
|
|||||||
*/
|
*/
|
||||||
import { Codec, renderContext, Context } from "./codec.js";
|
import { Codec, renderContext, Context } from "./codec.js";
|
||||||
|
|
||||||
export interface Timestamp {
|
export interface AbsoluteTime {
|
||||||
/**
|
/**
|
||||||
* Timestamp in milliseconds.
|
* Timestamp in milliseconds.
|
||||||
*/
|
*/
|
||||||
readonly t_ms: number | "never";
|
readonly t_ms: number | "never";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TalerProtocolTimestamp {
|
||||||
|
readonly t_s: number | "never";
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace TalerProtocolTimestamp {
|
||||||
|
export function now(): TalerProtocolTimestamp {
|
||||||
|
return AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function zero(): TalerProtocolTimestamp {
|
||||||
|
return {
|
||||||
|
t_s: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function never(): TalerProtocolTimestamp {
|
||||||
|
return {
|
||||||
|
t_s: "never",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fromSeconds(s: number): TalerProtocolTimestamp {
|
||||||
|
return {
|
||||||
|
t_s: s,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface Duration {
|
export interface Duration {
|
||||||
/**
|
/**
|
||||||
* Duration in milliseconds.
|
* Duration in milliseconds.
|
||||||
@ -37,40 +65,32 @@ export interface Duration {
|
|||||||
readonly d_ms: number | "forever";
|
readonly d_ms: number | "forever";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TalerProtocolDuration {
|
||||||
|
readonly d_us: number | "forever";
|
||||||
|
}
|
||||||
|
|
||||||
let timeshift = 0;
|
let timeshift = 0;
|
||||||
|
|
||||||
export function setDangerousTimetravel(dt: number): void {
|
export function setDangerousTimetravel(dt: number): void {
|
||||||
timeshift = dt;
|
timeshift = dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTimestampNow(): Timestamp {
|
|
||||||
return {
|
|
||||||
t_ms: new Date().getTime() + timeshift,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isTimestampExpired(t: Timestamp) {
|
|
||||||
return timestampCmp(t, getTimestampNow()) <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDurationRemaining(
|
|
||||||
deadline: Timestamp,
|
|
||||||
now = getTimestampNow(),
|
|
||||||
): Duration {
|
|
||||||
if (deadline.t_ms === "never") {
|
|
||||||
return { d_ms: "forever" };
|
|
||||||
}
|
|
||||||
if (now.t_ms === "never") {
|
|
||||||
throw Error("invalid argument for 'now'");
|
|
||||||
}
|
|
||||||
if (deadline.t_ms < now.t_ms) {
|
|
||||||
return { d_ms: 0 };
|
|
||||||
}
|
|
||||||
return { d_ms: deadline.t_ms - now.t_ms };
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace Duration {
|
export namespace Duration {
|
||||||
export const getRemaining = getDurationRemaining;
|
export function getRemaining(
|
||||||
|
deadline: AbsoluteTime,
|
||||||
|
now = AbsoluteTime.now(),
|
||||||
|
): Duration {
|
||||||
|
if (deadline.t_ms === "never") {
|
||||||
|
return { d_ms: "forever" };
|
||||||
|
}
|
||||||
|
if (now.t_ms === "never") {
|
||||||
|
throw Error("invalid argument for 'now'");
|
||||||
|
}
|
||||||
|
if (deadline.t_ms < now.t_ms) {
|
||||||
|
return { d_ms: 0 };
|
||||||
|
}
|
||||||
|
return { d_ms: deadline.t_ms - now.t_ms };
|
||||||
|
}
|
||||||
export function toIntegerYears(d: Duration): number {
|
export function toIntegerYears(d: Duration): number {
|
||||||
if (typeof d.d_ms !== "number") {
|
if (typeof d.d_ms !== "number") {
|
||||||
throw Error("infinite duration");
|
throw Error("infinite duration");
|
||||||
@ -81,33 +101,152 @@ export namespace Duration {
|
|||||||
export function getForever(): Duration {
|
export function getForever(): Duration {
|
||||||
return { d_ms: "forever" };
|
return { d_ms: "forever" };
|
||||||
}
|
}
|
||||||
|
export function fromTalerProtocolDuration(
|
||||||
|
d: TalerProtocolDuration,
|
||||||
|
): Duration {
|
||||||
|
if (d.d_us === "forever") {
|
||||||
|
return {
|
||||||
|
d_ms: "forever",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
d_ms: d.d_us / 1000,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace Timestamp {
|
export namespace AbsoluteTime {
|
||||||
export const now = getTimestampNow;
|
export function now(): AbsoluteTime {
|
||||||
export const min = timestampMin;
|
return {
|
||||||
export const isExpired = isTimestampExpired;
|
t_ms: new Date().getTime() + timeshift,
|
||||||
export const truncateToSecond = timestampTruncateToSecond;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function timestampMin(t1: Timestamp, t2: Timestamp): Timestamp {
|
export function never(): AbsoluteTime {
|
||||||
if (t1.t_ms === "never") {
|
return {
|
||||||
return { t_ms: t2.t_ms };
|
t_ms: "never",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (t2.t_ms === "never") {
|
|
||||||
return { t_ms: t2.t_ms };
|
|
||||||
}
|
|
||||||
return { t_ms: Math.min(t1.t_ms, t2.t_ms) };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function timestampMax(t1: Timestamp, t2: Timestamp): Timestamp {
|
export function cmp(t1: AbsoluteTime, t2: AbsoluteTime): number {
|
||||||
if (t1.t_ms === "never") {
|
if (t1.t_ms === "never") {
|
||||||
return { t_ms: "never" };
|
if (t2.t_ms === "never") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (t2.t_ms === "never") {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (t1.t_ms == t2.t_ms) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (t1.t_ms > t2.t_ms) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
if (t2.t_ms === "never") {
|
|
||||||
return { t_ms: "never" };
|
export function min(t1: AbsoluteTime, t2: AbsoluteTime): AbsoluteTime {
|
||||||
|
if (t1.t_ms === "never") {
|
||||||
|
return { t_ms: t2.t_ms };
|
||||||
|
}
|
||||||
|
if (t2.t_ms === "never") {
|
||||||
|
return { t_ms: t2.t_ms };
|
||||||
|
}
|
||||||
|
return { t_ms: Math.min(t1.t_ms, t2.t_ms) };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function max(t1: AbsoluteTime, t2: AbsoluteTime): AbsoluteTime {
|
||||||
|
if (t1.t_ms === "never") {
|
||||||
|
return { t_ms: "never" };
|
||||||
|
}
|
||||||
|
if (t2.t_ms === "never") {
|
||||||
|
return { t_ms: "never" };
|
||||||
|
}
|
||||||
|
return { t_ms: Math.max(t1.t_ms, t2.t_ms) };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function difference(t1: AbsoluteTime, t2: AbsoluteTime): Duration {
|
||||||
|
if (t1.t_ms === "never") {
|
||||||
|
return { d_ms: "forever" };
|
||||||
|
}
|
||||||
|
if (t2.t_ms === "never") {
|
||||||
|
return { d_ms: "forever" };
|
||||||
|
}
|
||||||
|
return { d_ms: Math.abs(t1.t_ms - t2.t_ms) };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isExpired(t: AbsoluteTime) {
|
||||||
|
return cmp(t, now()) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fromTimestamp(t: TalerProtocolTimestamp): AbsoluteTime {
|
||||||
|
if (t.t_s === "never") {
|
||||||
|
return { t_ms: "never" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
t_ms: t.t_s * 1000,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toTimestamp(at: AbsoluteTime): TalerProtocolTimestamp {
|
||||||
|
if (at.t_ms === "never") {
|
||||||
|
return { t_s: "never" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
t_s: Math.floor(at.t_ms / 1000),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBetween(
|
||||||
|
t: AbsoluteTime,
|
||||||
|
start: AbsoluteTime,
|
||||||
|
end: AbsoluteTime,
|
||||||
|
): boolean {
|
||||||
|
if (cmp(t, start) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cmp(t, end) > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toIsoString(t: AbsoluteTime): string {
|
||||||
|
if (t.t_ms === "never") {
|
||||||
|
return "<never>";
|
||||||
|
} else {
|
||||||
|
return new Date(t.t_ms).toISOString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addDuration(t1: AbsoluteTime, d: Duration): AbsoluteTime {
|
||||||
|
if (t1.t_ms === "never" || d.d_ms === "forever") {
|
||||||
|
return { t_ms: "never" };
|
||||||
|
}
|
||||||
|
return { t_ms: t1.t_ms + d.d_ms };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function subtractDuraction(
|
||||||
|
t1: AbsoluteTime,
|
||||||
|
d: Duration,
|
||||||
|
): AbsoluteTime {
|
||||||
|
if (t1.t_ms === "never") {
|
||||||
|
return { t_ms: "never" };
|
||||||
|
}
|
||||||
|
if (d.d_ms === "forever") {
|
||||||
|
return { t_ms: 0 };
|
||||||
|
}
|
||||||
|
return { t_ms: Math.max(0, t1.t_ms - d.d_ms) };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stringify(t: AbsoluteTime): string {
|
||||||
|
if (t.t_ms === "never") {
|
||||||
|
return "never";
|
||||||
|
}
|
||||||
|
return new Date(t.t_ms).toISOString();
|
||||||
}
|
}
|
||||||
return { t_ms: Math.max(t1.t_ms, t2.t_ms) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SECONDS = 1000;
|
const SECONDS = 1000;
|
||||||
@ -135,19 +274,6 @@ export function durationFromSpec(spec: {
|
|||||||
return { d_ms };
|
return { d_ms };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Truncate a timestamp so that that it represents a multiple
|
|
||||||
* of seconds. The timestamp is always rounded down.
|
|
||||||
*/
|
|
||||||
export function timestampTruncateToSecond(t1: Timestamp): Timestamp {
|
|
||||||
if (t1.t_ms === "never") {
|
|
||||||
return { t_ms: "never" };
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
t_ms: Math.floor(t1.t_ms / 1000) * 1000,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function durationMin(d1: Duration, d2: Duration): Duration {
|
export function durationMin(d1: Duration, d2: Duration): Duration {
|
||||||
if (d1.d_ms === "forever") {
|
if (d1.d_ms === "forever") {
|
||||||
return { d_ms: d2.d_ms };
|
return { d_ms: d2.d_ms };
|
||||||
@ -182,111 +308,33 @@ export function durationAdd(d1: Duration, d2: Duration): Duration {
|
|||||||
return { d_ms: d1.d_ms + d2.d_ms };
|
return { d_ms: d1.d_ms + d2.d_ms };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function timestampCmp(t1: Timestamp, t2: Timestamp): number {
|
export const codecForTimestamp: Codec<TalerProtocolTimestamp> = {
|
||||||
if (t1.t_ms === "never") {
|
decode(x: any, c?: Context): TalerProtocolTimestamp {
|
||||||
if (t2.t_ms === "never") {
|
const t_s = x.t_s;
|
||||||
return 0;
|
if (typeof t_s === "string") {
|
||||||
}
|
if (t_s === "never") {
|
||||||
return 1;
|
return { t_s: "never" };
|
||||||
}
|
|
||||||
if (t2.t_ms === "never") {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (t1.t_ms == t2.t_ms) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (t1.t_ms > t2.t_ms) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function timestampAddDuration(t1: Timestamp, d: Duration): Timestamp {
|
|
||||||
if (t1.t_ms === "never" || d.d_ms === "forever") {
|
|
||||||
return { t_ms: "never" };
|
|
||||||
}
|
|
||||||
return { t_ms: t1.t_ms + d.d_ms };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function timestampSubtractDuraction(
|
|
||||||
t1: Timestamp,
|
|
||||||
d: Duration,
|
|
||||||
): Timestamp {
|
|
||||||
if (t1.t_ms === "never") {
|
|
||||||
return { t_ms: "never" };
|
|
||||||
}
|
|
||||||
if (d.d_ms === "forever") {
|
|
||||||
return { t_ms: 0 };
|
|
||||||
}
|
|
||||||
return { t_ms: Math.max(0, t1.t_ms - d.d_ms) };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stringifyTimestamp(t: Timestamp): string {
|
|
||||||
if (t.t_ms === "never") {
|
|
||||||
return "never";
|
|
||||||
}
|
|
||||||
return new Date(t.t_ms).toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function timestampDifference(t1: Timestamp, t2: Timestamp): Duration {
|
|
||||||
if (t1.t_ms === "never") {
|
|
||||||
return { d_ms: "forever" };
|
|
||||||
}
|
|
||||||
if (t2.t_ms === "never") {
|
|
||||||
return { d_ms: "forever" };
|
|
||||||
}
|
|
||||||
return { d_ms: Math.abs(t1.t_ms - t2.t_ms) };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function timestampToIsoString(t: Timestamp): string {
|
|
||||||
if (t.t_ms === "never") {
|
|
||||||
return "<never>";
|
|
||||||
} else {
|
|
||||||
return new Date(t.t_ms).toISOString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function timestampIsBetween(
|
|
||||||
t: Timestamp,
|
|
||||||
start: Timestamp,
|
|
||||||
end: Timestamp,
|
|
||||||
): boolean {
|
|
||||||
if (timestampCmp(t, start) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (timestampCmp(t, end) > 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const codecForTimestamp: Codec<Timestamp> = {
|
|
||||||
decode(x: any, c?: Context): Timestamp {
|
|
||||||
const t_ms = x.t_ms;
|
|
||||||
if (typeof t_ms === "string") {
|
|
||||||
if (t_ms === "never") {
|
|
||||||
return { t_ms: "never" };
|
|
||||||
}
|
}
|
||||||
throw Error(`expected timestamp at ${renderContext(c)}`);
|
throw Error(`expected timestamp at ${renderContext(c)}`);
|
||||||
}
|
}
|
||||||
if (typeof t_ms === "number") {
|
if (typeof t_s === "number") {
|
||||||
return { t_ms };
|
return { t_s };
|
||||||
}
|
}
|
||||||
throw Error(`expected timestamp at ${renderContext(c)}`);
|
throw Error(`expected timestamp at ${renderContext(c)}`);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const codecForDuration: Codec<Duration> = {
|
export const codecForDuration: Codec<TalerProtocolDuration> = {
|
||||||
decode(x: any, c?: Context): Duration {
|
decode(x: any, c?: Context): TalerProtocolDuration {
|
||||||
const d_ms = x.d_ms;
|
const d_us = x.d_us;
|
||||||
if (typeof d_ms === "string") {
|
if (typeof d_us === "string") {
|
||||||
if (d_ms === "forever") {
|
if (d_us === "forever") {
|
||||||
return { d_ms: "forever" };
|
return { d_us: "forever" };
|
||||||
}
|
}
|
||||||
throw Error(`expected duration at ${renderContext(c)}`);
|
throw Error(`expected duration at ${renderContext(c)}`);
|
||||||
}
|
}
|
||||||
if (typeof d_ms === "number") {
|
if (typeof d_us === "number") {
|
||||||
return { d_ms };
|
return { d_us };
|
||||||
}
|
}
|
||||||
throw Error(`expected duration at ${renderContext(c)}`);
|
throw Error(`expected duration at ${renderContext(c)}`);
|
||||||
},
|
},
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { Timestamp } from "./time.js";
|
import { TalerProtocolTimestamp } from "./time.js";
|
||||||
import {
|
import {
|
||||||
AmountString,
|
AmountString,
|
||||||
Product,
|
Product,
|
||||||
@ -73,7 +73,7 @@ export interface TransactionCommon {
|
|||||||
type: TransactionType;
|
type: TransactionType;
|
||||||
|
|
||||||
// main timestamp of the transaction
|
// main timestamp of the transaction
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
// true if the transaction is still pending, false otherwise
|
// true if the transaction is still pending, false otherwise
|
||||||
// If a transaction is not longer pending, its timestamp will be updated,
|
// If a transaction is not longer pending, its timestamp will be updated,
|
||||||
|
@ -29,13 +29,13 @@ test("contract terms validation", (t) => {
|
|||||||
merchant_pub: "12345",
|
merchant_pub: "12345",
|
||||||
merchant: { name: "Foo" },
|
merchant: { name: "Foo" },
|
||||||
order_id: "test_order",
|
order_id: "test_order",
|
||||||
pay_deadline: { t_ms: 42 },
|
pay_deadline: { t_s: 42 },
|
||||||
wire_transfer_deadline: { t_ms: 42 },
|
wire_transfer_deadline: { t_s: 42 },
|
||||||
merchant_base_url: "https://example.com/pay",
|
merchant_base_url: "https://example.com/pay",
|
||||||
products: [],
|
products: [],
|
||||||
refund_deadline: { t_ms: 42 },
|
refund_deadline: { t_s: 42 },
|
||||||
summary: "hello",
|
summary: "hello",
|
||||||
timestamp: { t_ms: 42 },
|
timestamp: { t_s: 42 },
|
||||||
wire_method: "test",
|
wire_method: "test",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -71,13 +71,13 @@ test("contract terms validation (locations)", (t) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
order_id: "test_order",
|
order_id: "test_order",
|
||||||
pay_deadline: { t_ms: 42 },
|
pay_deadline: { t_s: 42 },
|
||||||
wire_transfer_deadline: { t_ms: 42 },
|
wire_transfer_deadline: { t_s: 42 },
|
||||||
merchant_base_url: "https://example.com/pay",
|
merchant_base_url: "https://example.com/pay",
|
||||||
products: [],
|
products: [],
|
||||||
refund_deadline: { t_ms: 42 },
|
refund_deadline: { t_s: 42 },
|
||||||
summary: "hello",
|
summary: "hello",
|
||||||
timestamp: { t_ms: 42 },
|
timestamp: { t_s: 42 },
|
||||||
wire_method: "test",
|
wire_method: "test",
|
||||||
delivery_location: {
|
delivery_location: {
|
||||||
country: "FR",
|
country: "FR",
|
||||||
|
@ -32,7 +32,11 @@ import {
|
|||||||
codecForAmountJson,
|
codecForAmountJson,
|
||||||
codecForAmountString,
|
codecForAmountString,
|
||||||
} from "./amounts.js";
|
} from "./amounts.js";
|
||||||
import { Timestamp, codecForTimestamp } from "./time.js";
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
|
codecForTimestamp,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
|
} from "./time.js";
|
||||||
import {
|
import {
|
||||||
buildCodecForObject,
|
buildCodecForObject,
|
||||||
codecForString,
|
codecForString,
|
||||||
@ -299,7 +303,7 @@ export interface PrepareTipResult {
|
|||||||
* Time when the tip will expire. After it expired, it can't be picked
|
* Time when the tip will expire. After it expired, it can't be picked
|
||||||
* up anymore.
|
* up anymore.
|
||||||
*/
|
*/
|
||||||
expirationTimestamp: Timestamp;
|
expirationTimestamp: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const codecForPrepareTipResult = (): Codec<PrepareTipResult> =>
|
export const codecForPrepareTipResult = (): Codec<PrepareTipResult> =>
|
||||||
@ -460,7 +464,7 @@ export interface TalerErrorDetails {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal information needed about a planchet for unblinding a signature.
|
* Minimal information needed about a planchet for unblinding a signature.
|
||||||
*
|
*
|
||||||
* Can be a withdrawal/tipping/refresh planchet.
|
* Can be a withdrawal/tipping/refresh planchet.
|
||||||
*/
|
*/
|
||||||
export interface PlanchetUnblindInfo {
|
export interface PlanchetUnblindInfo {
|
||||||
@ -527,8 +531,8 @@ export interface DepositInfo {
|
|||||||
coinPub: string;
|
coinPub: string;
|
||||||
coinPriv: string;
|
coinPriv: string;
|
||||||
spendAmount: AmountJson;
|
spendAmount: AmountJson;
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
refundDeadline: Timestamp;
|
refundDeadline: TalerProtocolTimestamp;
|
||||||
merchantPub: string;
|
merchantPub: string;
|
||||||
feeDeposit: AmountJson;
|
feeDeposit: AmountJson;
|
||||||
wireInfoHash: string;
|
wireInfoHash: string;
|
||||||
|
@ -478,7 +478,7 @@ class BankServiceBase {
|
|||||||
protected globalTestState: GlobalTestState,
|
protected globalTestState: GlobalTestState,
|
||||||
protected bankConfig: BankConfig,
|
protected bankConfig: BankConfig,
|
||||||
protected configFile: string,
|
protected configFile: string,
|
||||||
) { }
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -920,7 +920,7 @@ export class FakeBankService {
|
|||||||
private globalTestState: GlobalTestState,
|
private globalTestState: GlobalTestState,
|
||||||
private bankConfig: FakeBankConfig,
|
private bankConfig: FakeBankConfig,
|
||||||
private configFile: string,
|
private configFile: string,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
this.proc = this.globalTestState.spawnService(
|
this.proc = this.globalTestState.spawnService(
|
||||||
@ -1175,7 +1175,7 @@ export class ExchangeService implements ExchangeServiceInterface {
|
|||||||
private exchangeConfig: ExchangeConfig,
|
private exchangeConfig: ExchangeConfig,
|
||||||
private configFilename: string,
|
private configFilename: string,
|
||||||
private keyPair: EddsaKeyPair,
|
private keyPair: EddsaKeyPair,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
return this.exchangeConfig.name;
|
return this.exchangeConfig.name;
|
||||||
@ -1276,7 +1276,7 @@ export class ExchangeService implements ExchangeServiceInterface {
|
|||||||
accTargetType,
|
accTargetType,
|
||||||
`${this.exchangeConfig.currency}:0.01`,
|
`${this.exchangeConfig.currency}:0.01`,
|
||||||
`${this.exchangeConfig.currency}:0.01`,
|
`${this.exchangeConfig.currency}:0.01`,
|
||||||
// `${this.exchangeConfig.currency}:0.01`,
|
`${this.exchangeConfig.currency}:0.01`,
|
||||||
"upload",
|
"upload",
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -1398,7 +1398,7 @@ export class MerchantApiClient {
|
|||||||
constructor(
|
constructor(
|
||||||
private baseUrl: string,
|
private baseUrl: string,
|
||||||
public readonly auth: MerchantAuthConfiguration,
|
public readonly auth: MerchantAuthConfiguration,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
async changeAuth(auth: MerchantAuthConfiguration): Promise<void> {
|
async changeAuth(auth: MerchantAuthConfiguration): Promise<void> {
|
||||||
const url = new URL("private/auth", this.baseUrl);
|
const url = new URL("private/auth", this.baseUrl);
|
||||||
@ -1591,7 +1591,7 @@ export class MerchantService implements MerchantServiceInterface {
|
|||||||
private globalState: GlobalTestState,
|
private globalState: GlobalTestState,
|
||||||
private merchantConfig: MerchantConfig,
|
private merchantConfig: MerchantConfig,
|
||||||
private configFilename: string,
|
private configFilename: string,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
private currentTimetravel: Duration | undefined;
|
private currentTimetravel: Duration | undefined;
|
||||||
|
|
||||||
@ -1888,8 +1888,10 @@ export class WalletCli {
|
|||||||
const resp = await sh(
|
const resp = await sh(
|
||||||
self.globalTestState,
|
self.globalTestState,
|
||||||
`wallet-${self.name}`,
|
`wallet-${self.name}`,
|
||||||
`taler-wallet-cli ${self.timetravelArg ?? ""
|
`taler-wallet-cli ${
|
||||||
} --no-throttle -LTRACE --wallet-db '${self.dbfile
|
self.timetravelArg ?? ""
|
||||||
|
} --no-throttle -LTRACE --wallet-db '${
|
||||||
|
self.dbfile
|
||||||
}' api '${op}' ${shellWrap(JSON.stringify(payload))}`,
|
}' api '${op}' ${shellWrap(JSON.stringify(payload))}`,
|
||||||
);
|
);
|
||||||
console.log("--- wallet core response ---");
|
console.log("--- wallet core response ---");
|
||||||
|
@ -38,7 +38,7 @@ import {
|
|||||||
codecForAny,
|
codecForAny,
|
||||||
buildCodecForUnion,
|
buildCodecForUnion,
|
||||||
AmountString,
|
AmountString,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
CoinPublicKeyString,
|
CoinPublicKeyString,
|
||||||
EddsaPublicKeyString,
|
EddsaPublicKeyString,
|
||||||
codecForAmountString,
|
codecForAmountString,
|
||||||
@ -195,7 +195,7 @@ export interface RefundDetails {
|
|||||||
reason: string;
|
reason: string;
|
||||||
|
|
||||||
// when was the refund approved
|
// when was the refund approved
|
||||||
timestamp: Timestamp;
|
timestamp: AbsoluteTime;
|
||||||
|
|
||||||
// Total amount that was refunded (minus a refund fee).
|
// Total amount that was refunded (minus a refund fee).
|
||||||
amount: AmountString;
|
amount: AmountString;
|
||||||
@ -209,7 +209,7 @@ export interface TransactionWireTransfer {
|
|||||||
wtid: string;
|
wtid: string;
|
||||||
|
|
||||||
// execution time of the wire transfer
|
// execution time of the wire transfer
|
||||||
execution_time: Timestamp;
|
execution_time: AbsoluteTime;
|
||||||
|
|
||||||
// Total amount that has been wire transferred
|
// Total amount that has been wire transferred
|
||||||
// to the merchant
|
// to the merchant
|
||||||
@ -247,10 +247,10 @@ export interface ReserveStatusEntry {
|
|||||||
reserve_pub: string;
|
reserve_pub: string;
|
||||||
|
|
||||||
// Timestamp when it was established
|
// Timestamp when it was established
|
||||||
creation_time: Timestamp;
|
creation_time: AbsoluteTime;
|
||||||
|
|
||||||
// Timestamp when it expires
|
// Timestamp when it expires
|
||||||
expiration_time: Timestamp;
|
expiration_time: AbsoluteTime;
|
||||||
|
|
||||||
// Initial amount as per reserve creation call
|
// Initial amount as per reserve creation call
|
||||||
merchant_initial_amount: AmountString;
|
merchant_initial_amount: AmountString;
|
||||||
@ -281,7 +281,7 @@ export interface TipCreateConfirmation {
|
|||||||
tip_status_url: string;
|
tip_status_url: string;
|
||||||
|
|
||||||
// when does the tip expire
|
// when does the tip expire
|
||||||
tip_expiration: Timestamp;
|
tip_expiration: AbsoluteTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TipCreateRequest {
|
export interface TipCreateRequest {
|
||||||
|
@ -32,6 +32,7 @@ import {
|
|||||||
BankApi,
|
BankApi,
|
||||||
BankAccessApi,
|
BankAccessApi,
|
||||||
CreditDebitIndicator,
|
CreditDebitIndicator,
|
||||||
|
OperationFailedError,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,8 +107,7 @@ export async function runBankApiTest(t: GlobalTestState) {
|
|||||||
const e = await t.assertThrowsAsync(async () => {
|
const e = await t.assertThrowsAsync(async () => {
|
||||||
await BankApi.registerAccount(bank, "user1", "pw1");
|
await BankApi.registerAccount(bank, "user1", "pw1");
|
||||||
});
|
});
|
||||||
t.assertAxiosError(e);
|
t.assertTrue(e.details.httpStatusCode === 409);
|
||||||
t.assertTrue(e.response?.status === 409);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let balResp = await BankAccessApi.getAccountBalance(bank, bankUser);
|
let balResp = await BankAccessApi.getAccountBalance(bank, bankUser);
|
||||||
|
@ -18,11 +18,11 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
codecForExchangeKeysJson,
|
codecForExchangeKeysJson,
|
||||||
DenominationPubKey,
|
DenominationPubKey,
|
||||||
Duration,
|
Duration,
|
||||||
durationFromSpec,
|
durationFromSpec,
|
||||||
stringifyTimestamp,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
NodeHttpLib,
|
NodeHttpLib,
|
||||||
@ -174,27 +174,37 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
|
|||||||
const denomPubs1 = keys1.denoms.map((x) => {
|
const denomPubs1 = keys1.denoms.map((x) => {
|
||||||
return {
|
return {
|
||||||
denomPub: x.denom_pub,
|
denomPub: x.denom_pub,
|
||||||
expireDeposit: stringifyTimestamp(x.stamp_expire_deposit),
|
expireDeposit: AbsoluteTime.stringify(
|
||||||
|
AbsoluteTime.fromTimestamp(x.stamp_expire_deposit),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const denomPubs2 = keys2.denoms.map((x) => {
|
const denomPubs2 = keys2.denoms.map((x) => {
|
||||||
return {
|
return {
|
||||||
denomPub: x.denom_pub,
|
denomPub: x.denom_pub,
|
||||||
expireDeposit: stringifyTimestamp(x.stamp_expire_deposit),
|
expireDeposit: AbsoluteTime.stringify(
|
||||||
|
AbsoluteTime.fromTimestamp(x.stamp_expire_deposit),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const dps2 = new Set(denomPubs2.map((x) => x.denomPub));
|
const dps2 = new Set(denomPubs2.map((x) => x.denomPub));
|
||||||
|
|
||||||
console.log("=== KEYS RESPONSE 1 ===");
|
console.log("=== KEYS RESPONSE 1 ===");
|
||||||
|
|
||||||
console.log("list issue date", stringifyTimestamp(keys1.list_issue_date));
|
console.log(
|
||||||
|
"list issue date",
|
||||||
|
AbsoluteTime.stringify(AbsoluteTime.fromTimestamp(keys1.list_issue_date)),
|
||||||
|
);
|
||||||
console.log("num denoms", keys1.denoms.length);
|
console.log("num denoms", keys1.denoms.length);
|
||||||
console.log("denoms", JSON.stringify(denomPubs1, undefined, 2));
|
console.log("denoms", JSON.stringify(denomPubs1, undefined, 2));
|
||||||
|
|
||||||
console.log("=== KEYS RESPONSE 2 ===");
|
console.log("=== KEYS RESPONSE 2 ===");
|
||||||
|
|
||||||
console.log("list issue date", stringifyTimestamp(keys2.list_issue_date));
|
console.log(
|
||||||
|
"list issue date",
|
||||||
|
AbsoluteTime.stringify(AbsoluteTime.fromTimestamp(keys2.list_issue_date)),
|
||||||
|
);
|
||||||
console.log("num denoms", keys2.denoms.length);
|
console.log("num denoms", keys2.denoms.length);
|
||||||
console.log("denoms", JSON.stringify(denomPubs2, undefined, 2));
|
console.log("denoms", JSON.stringify(denomPubs2, undefined, 2));
|
||||||
|
|
||||||
@ -214,8 +224,8 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
|
|||||||
`denomination with public key ${da.denomPub} is not present in new /keys response`,
|
`denomination with public key ${da.denomPub} is not present in new /keys response`,
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
`the new /keys response was issued ${stringifyTimestamp(
|
`the new /keys response was issued ${AbsoluteTime.stringify(
|
||||||
keys2.list_issue_date,
|
AbsoluteTime.fromTimestamp(keys2.list_issue_date),
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
|
@ -18,9 +18,8 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
ContractTerms,
|
ContractTerms,
|
||||||
getTimestampNow,
|
|
||||||
timestampTruncateToSecond,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
WalletApiOperation,
|
WalletApiOperation,
|
||||||
@ -277,7 +276,7 @@ export async function runLibeufinBasicTest(t: GlobalTestState) {
|
|||||||
summary: "Buy me!",
|
summary: "Buy me!",
|
||||||
amount: "EUR:5",
|
amount: "EUR:5",
|
||||||
fulfillment_url: "taler://fulfillment-success/thx",
|
fulfillment_url: "taler://fulfillment-success/thx",
|
||||||
wire_transfer_deadline: timestampTruncateToSecond(getTimestampNow()),
|
wire_transfer_deadline: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
|
||||||
};
|
};
|
||||||
|
|
||||||
await makeTestPayment(t, { wallet, merchant, order });
|
await makeTestPayment(t, { wallet, merchant, order });
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
MerchantApiClient,
|
MerchantApiClient,
|
||||||
MerchantService,
|
MerchantService,
|
||||||
setupDb,
|
setupDb,
|
||||||
getPayto
|
getPayto,
|
||||||
} from "../harness/harness.js";
|
} from "../harness/harness.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,7 +18,10 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
|
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
|
||||||
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
|
import {
|
||||||
|
createSimpleTestkudosEnvironment,
|
||||||
|
withdrawViaBank,
|
||||||
|
} from "../harness/helpers.js";
|
||||||
import { durationFromSpec } from "@gnu-taler/taler-util";
|
import { durationFromSpec } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
@ -28,12 +31,8 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
|||||||
export async function runRefundAutoTest(t: GlobalTestState) {
|
export async function runRefundAutoTest(t: GlobalTestState) {
|
||||||
// Set up test environment
|
// Set up test environment
|
||||||
|
|
||||||
const {
|
const { wallet, bank, exchange, merchant } =
|
||||||
wallet,
|
await createSimpleTestkudosEnvironment(t);
|
||||||
bank,
|
|
||||||
exchange,
|
|
||||||
merchant,
|
|
||||||
} = await createSimpleTestkudosEnvironment(t);
|
|
||||||
|
|
||||||
// Withdraw digital cash into the wallet.
|
// Withdraw digital cash into the wallet.
|
||||||
|
|
||||||
@ -46,7 +45,7 @@ export async function runRefundAutoTest(t: GlobalTestState) {
|
|||||||
amount: "TESTKUDOS:5",
|
amount: "TESTKUDOS:5",
|
||||||
fulfillment_url: "taler://fulfillment-success/thx",
|
fulfillment_url: "taler://fulfillment-success/thx",
|
||||||
auto_refund: {
|
auto_refund: {
|
||||||
d_ms: 3000,
|
d_us: 3000 * 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
refund_delay: durationFromSpec({ minutes: 5 }),
|
refund_delay: durationFromSpec({ minutes: 5 }),
|
||||||
|
@ -24,10 +24,8 @@ import {
|
|||||||
applyTimeTravel,
|
applyTimeTravel,
|
||||||
} from "../harness/helpers.js";
|
} from "../harness/helpers.js";
|
||||||
import {
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
durationFromSpec,
|
durationFromSpec,
|
||||||
timestampAddDuration,
|
|
||||||
getTimestampNow,
|
|
||||||
timestampTruncateToSecond,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
@ -55,9 +53,9 @@ export async function runRefundGoneTest(t: GlobalTestState) {
|
|||||||
summary: "Buy me!",
|
summary: "Buy me!",
|
||||||
amount: "TESTKUDOS:5",
|
amount: "TESTKUDOS:5",
|
||||||
fulfillment_url: "taler://fulfillment-success/thx",
|
fulfillment_url: "taler://fulfillment-success/thx",
|
||||||
pay_deadline: timestampTruncateToSecond(
|
pay_deadline: AbsoluteTime.toTimestamp(
|
||||||
timestampAddDuration(
|
AbsoluteTime.addDuration(
|
||||||
getTimestampNow(),
|
AbsoluteTime.now(),
|
||||||
durationFromSpec({
|
durationFromSpec({
|
||||||
minutes: 10,
|
minutes: 10,
|
||||||
}),
|
}),
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
AmountString,
|
AmountString,
|
||||||
buildCodecForObject,
|
buildCodecForObject,
|
||||||
Codec,
|
Codec,
|
||||||
|
codecForAny,
|
||||||
codecForString,
|
codecForString,
|
||||||
encodeCrock,
|
encodeCrock,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
@ -102,15 +103,16 @@ export namespace BankApi {
|
|||||||
const resp = await bank.http.postJson(url.href, { username, password });
|
const resp = await bank.http.postJson(url.href, { username, password });
|
||||||
let paytoUri = `payto://x-taler-bank/localhost/${username}`;
|
let paytoUri = `payto://x-taler-bank/localhost/${username}`;
|
||||||
if (resp.status !== 200 && resp.status !== 202) {
|
if (resp.status !== 200 && resp.status !== 202) {
|
||||||
logger.error(`${j2s(await resp.json())}`)
|
logger.error(`${j2s(await resp.json())}`);
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny());
|
||||||
|
// LibEuFin demobank returns payto URI in response
|
||||||
|
if (respJson.paytoUri) {
|
||||||
|
paytoUri = respJson.paytoUri;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const respJson = await resp.json();
|
const respJson = await resp.json();
|
||||||
// LibEuFin demobank returns payto URI in response
|
|
||||||
if (respJson.paytoUri) {
|
|
||||||
paytoUri = respJson.paytoUri;
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return {
|
return {
|
||||||
password,
|
password,
|
||||||
|
@ -34,7 +34,8 @@ import {
|
|||||||
BalancesResponse,
|
BalancesResponse,
|
||||||
AmountJson,
|
AmountJson,
|
||||||
DenominationPubKey,
|
DenominationPubKey,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { CryptoApi } from "./crypto/workers/cryptoApi.js";
|
import { CryptoApi } from "./crypto/workers/cryptoApi.js";
|
||||||
import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
|
import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
|
||||||
@ -165,22 +166,22 @@ export interface DenomInfo {
|
|||||||
/**
|
/**
|
||||||
* Validity start date of the denomination.
|
* Validity start date of the denomination.
|
||||||
*/
|
*/
|
||||||
stampStart: Timestamp;
|
stampStart: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date after which the currency can't be withdrawn anymore.
|
* Date after which the currency can't be withdrawn anymore.
|
||||||
*/
|
*/
|
||||||
stampExpireWithdraw: Timestamp;
|
stampExpireWithdraw: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date after the denomination officially doesn't exist anymore.
|
* Date after the denomination officially doesn't exist anymore.
|
||||||
*/
|
*/
|
||||||
stampExpireLegal: Timestamp;
|
stampExpireLegal: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data after which coins of this denomination can't be deposited anymore.
|
* Data after which coins of this denomination can't be deposited anymore.
|
||||||
*/
|
*/
|
||||||
stampExpireDeposit: Timestamp;
|
stampExpireDeposit: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NotificationListener = (n: WalletNotification) => void;
|
export type NotificationListener = (n: WalletNotification) => void;
|
||||||
|
@ -207,7 +207,7 @@ export class CryptoApi {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
ws.terminationTimerHandle = timer.after(15 * 1000, destroy);
|
ws.terminationTimerHandle = timer.after(15 * 1000, destroy);
|
||||||
ws.terminationTimerHandle.unref();
|
//ws.terminationTimerHandle.unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWorkerError(ws: WorkerState, e: any): void {
|
handleWorkerError(ws: WorkerState, e: any): void {
|
||||||
|
@ -67,13 +67,11 @@ import {
|
|||||||
setupWithdrawPlanchet,
|
setupWithdrawPlanchet,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
TalerSignaturePurpose,
|
TalerSignaturePurpose,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
timestampTruncateToSecond,
|
|
||||||
typedArrayConcat,
|
|
||||||
BlindedDenominationSignature,
|
BlindedDenominationSignature,
|
||||||
RsaUnblindedSignature,
|
|
||||||
UnblindedSignature,
|
UnblindedSignature,
|
||||||
PlanchetUnblindInfo,
|
PlanchetUnblindInfo,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import bigint from "big-integer";
|
import bigint from "big-integer";
|
||||||
import { DenominationRecord, WireFee } from "../../db.js";
|
import { DenominationRecord, WireFee } from "../../db.js";
|
||||||
@ -110,18 +108,16 @@ function amountToBuffer(amount: AmountJson): Uint8Array {
|
|||||||
return u8buf;
|
return u8buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
function timestampRoundedToBuffer(ts: Timestamp): Uint8Array {
|
function timestampRoundedToBuffer(ts: TalerProtocolTimestamp): Uint8Array {
|
||||||
const b = new ArrayBuffer(8);
|
const b = new ArrayBuffer(8);
|
||||||
const v = new DataView(b);
|
const v = new DataView(b);
|
||||||
const tsRounded = timestampTruncateToSecond(ts);
|
// The buffer we sign over represents the timestamp in microseconds.
|
||||||
if (typeof v.setBigUint64 !== "undefined") {
|
if (typeof v.setBigUint64 !== "undefined") {
|
||||||
const s = BigInt(tsRounded.t_ms) * BigInt(1000);
|
const s = BigInt(ts.t_s) * BigInt(1000 * 1000);
|
||||||
v.setBigUint64(0, s);
|
v.setBigUint64(0, s);
|
||||||
} else {
|
} else {
|
||||||
const s =
|
const s =
|
||||||
tsRounded.t_ms === "never"
|
ts.t_s === "never" ? bigint.zero : bigint(ts.t_s).multiply(1000 * 1000);
|
||||||
? bigint.zero
|
|
||||||
: bigint(tsRounded.t_ms).times(1000);
|
|
||||||
const arr = s.toArray(2 ** 8).value;
|
const arr = s.toArray(2 ** 8).value;
|
||||||
let offset = 8 - arr.length;
|
let offset = 8 - arr.length;
|
||||||
for (let i = 0; i < arr.length; i++) {
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
@ -36,9 +36,10 @@ import {
|
|||||||
Product,
|
Product,
|
||||||
RefreshReason,
|
RefreshReason,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Timestamp,
|
|
||||||
UnblindedSignature,
|
UnblindedSignature,
|
||||||
CoinEnvelope,
|
CoinEnvelope,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
|
TalerProtocolDuration,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { RetryInfo } from "./util/retries.js";
|
import { RetryInfo } from "./util/retries.js";
|
||||||
import { PayCoinSelection } from "./util/coinSelection.js";
|
import { PayCoinSelection } from "./util/coinSelection.js";
|
||||||
@ -152,7 +153,7 @@ export interface ReserveRecord {
|
|||||||
/**
|
/**
|
||||||
* Time when the reserve was created.
|
* Time when the reserve was created.
|
||||||
*/
|
*/
|
||||||
timestampCreated: Timestamp;
|
timestampCreated: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the information about this reserve was posted to the bank.
|
* Time when the information about this reserve was posted to the bank.
|
||||||
@ -161,14 +162,14 @@ export interface ReserveRecord {
|
|||||||
*
|
*
|
||||||
* Set to undefined if that hasn't happened yet.
|
* Set to undefined if that hasn't happened yet.
|
||||||
*/
|
*/
|
||||||
timestampReserveInfoPosted: Timestamp | undefined;
|
timestampReserveInfoPosted: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the reserve was confirmed by the bank.
|
* Time when the reserve was confirmed by the bank.
|
||||||
*
|
*
|
||||||
* Set to undefined if not confirmed yet.
|
* Set to undefined if not confirmed yet.
|
||||||
*/
|
*/
|
||||||
timestampBankConfirmed: Timestamp | undefined;
|
timestampBankConfirmed: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire information (as payto URI) for the bank account that
|
* Wire information (as payto URI) for the bank account that
|
||||||
@ -217,11 +218,6 @@ export interface ReserveRecord {
|
|||||||
*/
|
*/
|
||||||
operationStatus: OperationStatus;
|
operationStatus: OperationStatus;
|
||||||
|
|
||||||
/**
|
|
||||||
* Time of the last successful status query.
|
|
||||||
*/
|
|
||||||
lastSuccessfulStatusQuery: Timestamp | undefined;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retry info, in case the reserve needs to be processed again
|
* Retry info, in case the reserve needs to be processed again
|
||||||
* later, either due to an error or because the wallet needs to
|
* later, either due to an error or because the wallet needs to
|
||||||
@ -350,22 +346,22 @@ export interface DenominationRecord {
|
|||||||
/**
|
/**
|
||||||
* Validity start date of the denomination.
|
* Validity start date of the denomination.
|
||||||
*/
|
*/
|
||||||
stampStart: Timestamp;
|
stampStart: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date after which the currency can't be withdrawn anymore.
|
* Date after which the currency can't be withdrawn anymore.
|
||||||
*/
|
*/
|
||||||
stampExpireWithdraw: Timestamp;
|
stampExpireWithdraw: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date after the denomination officially doesn't exist anymore.
|
* Date after the denomination officially doesn't exist anymore.
|
||||||
*/
|
*/
|
||||||
stampExpireLegal: Timestamp;
|
stampExpireLegal: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data after which coins of this denomination can't be deposited anymore.
|
* Data after which coins of this denomination can't be deposited anymore.
|
||||||
*/
|
*/
|
||||||
stampExpireDeposit: Timestamp;
|
stampExpireDeposit: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature by the exchange's master key over the denomination
|
* Signature by the exchange's master key over the denomination
|
||||||
@ -407,7 +403,7 @@ export interface DenominationRecord {
|
|||||||
* Latest list issue date of the "/keys" response
|
* Latest list issue date of the "/keys" response
|
||||||
* that includes this denomination.
|
* that includes this denomination.
|
||||||
*/
|
*/
|
||||||
listIssueDate: Timestamp;
|
listIssueDate: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -441,7 +437,7 @@ export interface ExchangeDetailsRecord {
|
|||||||
*/
|
*/
|
||||||
protocolVersion: string;
|
protocolVersion: string;
|
||||||
|
|
||||||
reserveClosingDelay: Duration;
|
reserveClosingDelay: TalerProtocolDuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signing keys we got from the exchange, can also contain
|
* Signing keys we got from the exchange, can also contain
|
||||||
@ -478,7 +474,7 @@ export interface ExchangeDetailsRecord {
|
|||||||
*
|
*
|
||||||
* Used during backup merging.
|
* Used during backup merging.
|
||||||
*/
|
*/
|
||||||
termsOfServiceAcceptedTimestamp: Timestamp | undefined;
|
termsOfServiceAcceptedTimestamp: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
wireInfo: WireInfo;
|
wireInfo: WireInfo;
|
||||||
}
|
}
|
||||||
@ -503,7 +499,7 @@ export interface ExchangeDetailsPointer {
|
|||||||
* Timestamp when the (masterPublicKey, currency) pointer
|
* Timestamp when the (masterPublicKey, currency) pointer
|
||||||
* has been updated.
|
* has been updated.
|
||||||
*/
|
*/
|
||||||
updateClock: Timestamp;
|
updateClock: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -528,14 +524,14 @@ export interface ExchangeRecord {
|
|||||||
/**
|
/**
|
||||||
* Last time when the exchange was updated.
|
* Last time when the exchange was updated.
|
||||||
*/
|
*/
|
||||||
lastUpdate: Timestamp | undefined;
|
lastUpdate: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next scheduled update for the exchange.
|
* Next scheduled update for the exchange.
|
||||||
*
|
*
|
||||||
* (This field must always be present, so we can index on the timestamp.)
|
* (This field must always be present, so we can index on the timestamp.)
|
||||||
*/
|
*/
|
||||||
nextUpdate: Timestamp;
|
nextUpdate: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next time that we should check if coins need to be refreshed.
|
* Next time that we should check if coins need to be refreshed.
|
||||||
@ -543,7 +539,7 @@ export interface ExchangeRecord {
|
|||||||
* Updated whenever the exchange's denominations are updated or when
|
* Updated whenever the exchange's denominations are updated or when
|
||||||
* the refresh check has been done.
|
* the refresh check has been done.
|
||||||
*/
|
*/
|
||||||
nextRefreshCheck: Timestamp;
|
nextRefreshCheck: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last error (if any) for fetching updated information about the
|
* Last error (if any) for fetching updated information about the
|
||||||
@ -793,7 +789,7 @@ export interface ProposalRecord {
|
|||||||
* Timestamp (in ms) of when the record
|
* Timestamp (in ms) of when the record
|
||||||
* was created.
|
* was created.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private key for the nonce.
|
* Private key for the nonce.
|
||||||
@ -837,7 +833,7 @@ export interface TipRecord {
|
|||||||
* Has the user accepted the tip? Only after the tip has been accepted coins
|
* Has the user accepted the tip? Only after the tip has been accepted coins
|
||||||
* withdrawn from the tip may be used.
|
* withdrawn from the tip may be used.
|
||||||
*/
|
*/
|
||||||
acceptedTimestamp: Timestamp | undefined;
|
acceptedTimestamp: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tipped amount.
|
* The tipped amount.
|
||||||
@ -849,7 +845,7 @@ export interface TipRecord {
|
|||||||
/**
|
/**
|
||||||
* Timestamp, the tip can't be picked up anymore after this deadline.
|
* Timestamp, the tip can't be picked up anymore after this deadline.
|
||||||
*/
|
*/
|
||||||
tipExpiration: Timestamp;
|
tipExpiration: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The exchange that will sign our coins, chosen by the merchant.
|
* The exchange that will sign our coins, chosen by the merchant.
|
||||||
@ -884,13 +880,13 @@ export interface TipRecord {
|
|||||||
*/
|
*/
|
||||||
merchantTipId: string;
|
merchantTipId: string;
|
||||||
|
|
||||||
createdTimestamp: Timestamp;
|
createdTimestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp for when the wallet finished picking up the tip
|
* Timestamp for when the wallet finished picking up the tip
|
||||||
* from the merchant.
|
* from the merchant.
|
||||||
*/
|
*/
|
||||||
pickedUpTimestamp: Timestamp | undefined;
|
pickedUpTimestamp: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retry info, even present when the operation isn't active to allow indexing
|
* Retry info, even present when the operation isn't active to allow indexing
|
||||||
@ -959,12 +955,12 @@ export interface RefreshGroupRecord {
|
|||||||
*/
|
*/
|
||||||
statusPerCoin: RefreshCoinStatus[];
|
statusPerCoin: RefreshCoinStatus[];
|
||||||
|
|
||||||
timestampCreated: Timestamp;
|
timestampCreated: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp when the refresh session finished.
|
* Timestamp when the refresh session finished.
|
||||||
*/
|
*/
|
||||||
timestampFinished: Timestamp | undefined;
|
timestampFinished: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No coins are pending, but at least one is frozen.
|
* No coins are pending, but at least one is frozen.
|
||||||
@ -1025,12 +1021,12 @@ export interface WireFee {
|
|||||||
/**
|
/**
|
||||||
* Start date of the fee.
|
* Start date of the fee.
|
||||||
*/
|
*/
|
||||||
startStamp: Timestamp;
|
startStamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End date of the fee.
|
* End date of the fee.
|
||||||
*/
|
*/
|
||||||
endStamp: Timestamp;
|
endStamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature made by the exchange master key.
|
* Signature made by the exchange master key.
|
||||||
@ -1054,12 +1050,12 @@ export type WalletRefundItem =
|
|||||||
|
|
||||||
export interface WalletRefundItemCommon {
|
export interface WalletRefundItemCommon {
|
||||||
// Execution time as claimed by the merchant
|
// Execution time as claimed by the merchant
|
||||||
executionTime: Timestamp;
|
executionTime: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the wallet became aware of the refund.
|
* Time when the wallet became aware of the refund.
|
||||||
*/
|
*/
|
||||||
obtainedTime: Timestamp;
|
obtainedTime: TalerProtocolTimestamp;
|
||||||
|
|
||||||
refundAmount: AmountJson;
|
refundAmount: AmountJson;
|
||||||
|
|
||||||
@ -1141,14 +1137,14 @@ export interface WalletContractData {
|
|||||||
orderId: string;
|
orderId: string;
|
||||||
merchantBaseUrl: string;
|
merchantBaseUrl: string;
|
||||||
summary: string;
|
summary: string;
|
||||||
autoRefund: Duration | undefined;
|
autoRefund: TalerProtocolDuration | undefined;
|
||||||
maxWireFee: AmountJson;
|
maxWireFee: AmountJson;
|
||||||
wireFeeAmortization: number;
|
wireFeeAmortization: number;
|
||||||
payDeadline: Timestamp;
|
payDeadline: TalerProtocolTimestamp;
|
||||||
refundDeadline: Timestamp;
|
refundDeadline: TalerProtocolTimestamp;
|
||||||
allowedAuditors: AllowedAuditorInfo[];
|
allowedAuditors: AllowedAuditorInfo[];
|
||||||
allowedExchanges: AllowedExchangeInfo[];
|
allowedExchanges: AllowedExchangeInfo[];
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
wireMethod: string;
|
wireMethod: string;
|
||||||
wireInfoHash: string;
|
wireInfoHash: string;
|
||||||
maxDepositFee: AmountJson;
|
maxDepositFee: AmountJson;
|
||||||
@ -1215,8 +1211,10 @@ export interface PurchaseRecord {
|
|||||||
/**
|
/**
|
||||||
* Timestamp of the first time that sending a payment to the merchant
|
* Timestamp of the first time that sending a payment to the merchant
|
||||||
* for this purchase was successful.
|
* for this purchase was successful.
|
||||||
|
*
|
||||||
|
* FIXME: Does this need to be a timestamp, doensn't boolean suffice?
|
||||||
*/
|
*/
|
||||||
timestampFirstSuccessfulPay: Timestamp | undefined;
|
timestampFirstSuccessfulPay: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
merchantPaySig: string | undefined;
|
merchantPaySig: string | undefined;
|
||||||
|
|
||||||
@ -1224,7 +1222,7 @@ export interface PurchaseRecord {
|
|||||||
* When was the purchase made?
|
* When was the purchase made?
|
||||||
* Refers to the time that the user accepted.
|
* Refers to the time that the user accepted.
|
||||||
*/
|
*/
|
||||||
timestampAccept: Timestamp;
|
timestampAccept: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pending refunds for the purchase. A refund is pending
|
* Pending refunds for the purchase. A refund is pending
|
||||||
@ -1236,7 +1234,7 @@ export interface PurchaseRecord {
|
|||||||
* When was the last refund made?
|
* When was the last refund made?
|
||||||
* Set to 0 if no refund was made on the purchase.
|
* Set to 0 if no refund was made on the purchase.
|
||||||
*/
|
*/
|
||||||
timestampLastRefundStatus: Timestamp | undefined;
|
timestampLastRefundStatus: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last session signature that we submitted to /pay (if any).
|
* Last session signature that we submitted to /pay (if any).
|
||||||
@ -1273,7 +1271,7 @@ export interface PurchaseRecord {
|
|||||||
/**
|
/**
|
||||||
* Continue querying the refund status until this deadline has expired.
|
* Continue querying the refund status until this deadline has expired.
|
||||||
*/
|
*/
|
||||||
autoRefundDeadline: Timestamp | undefined;
|
autoRefundDeadline: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the payment frozen? I.e. did we encounter
|
* Is the payment frozen? I.e. did we encounter
|
||||||
@ -1308,12 +1306,12 @@ export interface WalletBackupConfState {
|
|||||||
/**
|
/**
|
||||||
* Timestamp stored in the last backup.
|
* Timestamp stored in the last backup.
|
||||||
*/
|
*/
|
||||||
lastBackupTimestamp?: Timestamp;
|
lastBackupTimestamp?: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last time we tried to do a backup.
|
* Last time we tried to do a backup.
|
||||||
*/
|
*/
|
||||||
lastBackupCheckTimestamp?: Timestamp;
|
lastBackupCheckTimestamp?: TalerProtocolTimestamp;
|
||||||
lastBackupNonce?: string;
|
lastBackupNonce?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1362,14 +1360,14 @@ export interface WithdrawalGroupRecord {
|
|||||||
* When was the withdrawal operation started started?
|
* When was the withdrawal operation started started?
|
||||||
* Timestamp in milliseconds.
|
* Timestamp in milliseconds.
|
||||||
*/
|
*/
|
||||||
timestampStart: Timestamp;
|
timestampStart: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When was the withdrawal operation completed?
|
* When was the withdrawal operation completed?
|
||||||
*
|
*
|
||||||
* FIXME: We should probably drop this and introduce an OperationStatus field.
|
* FIXME: We should probably drop this and introduce an OperationStatus field.
|
||||||
*/
|
*/
|
||||||
timestampFinish?: Timestamp;
|
timestampFinish?: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operation status of the withdrawal group.
|
* Operation status of the withdrawal group.
|
||||||
@ -1429,9 +1427,9 @@ export interface RecoupGroupRecord {
|
|||||||
*/
|
*/
|
||||||
recoupGroupId: string;
|
recoupGroupId: string;
|
||||||
|
|
||||||
timestampStarted: Timestamp;
|
timestampStarted: TalerProtocolTimestamp;
|
||||||
|
|
||||||
timestampFinished: Timestamp | undefined;
|
timestampFinished: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public keys that identify the coins being recouped
|
* Public keys that identify the coins being recouped
|
||||||
@ -1482,7 +1480,7 @@ export type BackupProviderState =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
tag: BackupProviderStateTag.Ready;
|
tag: BackupProviderStateTag.Ready;
|
||||||
nextBackupTimestamp: Timestamp;
|
nextBackupTimestamp: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
tag: BackupProviderStateTag.Retrying;
|
tag: BackupProviderStateTag.Retrying;
|
||||||
@ -1529,7 +1527,7 @@ export interface BackupProviderRecord {
|
|||||||
* Does NOT correspond to the timestamp of the backup,
|
* Does NOT correspond to the timestamp of the backup,
|
||||||
* which only changes when the backup content changes.
|
* which only changes when the backup content changes.
|
||||||
*/
|
*/
|
||||||
lastBackupCycleTimestamp?: Timestamp;
|
lastBackupCycleTimestamp?: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proposal that we're currently trying to pay for.
|
* Proposal that we're currently trying to pay for.
|
||||||
@ -1594,9 +1592,9 @@ export interface DepositGroupRecord {
|
|||||||
|
|
||||||
depositedPerCoin: boolean[];
|
depositedPerCoin: boolean[];
|
||||||
|
|
||||||
timestampCreated: Timestamp;
|
timestampCreated: TalerProtocolTimestamp;
|
||||||
|
|
||||||
timestampFinished: Timestamp | undefined;
|
timestampFinished: TalerProtocolTimestamp | undefined;
|
||||||
|
|
||||||
operationStatus: OperationStatus;
|
operationStatus: OperationStatus;
|
||||||
|
|
||||||
@ -1618,14 +1616,14 @@ export interface GhostDepositGroupRecord {
|
|||||||
* When multiple deposits for the same contract terms hash
|
* When multiple deposits for the same contract terms hash
|
||||||
* have a different timestamp, we choose the earliest one.
|
* have a different timestamp, we choose the earliest one.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
contractTermsHash: string;
|
contractTermsHash: string;
|
||||||
|
|
||||||
deposits: {
|
deposits: {
|
||||||
coinPub: string;
|
coinPub: string;
|
||||||
amount: AmountString;
|
amount: AmountString;
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
depositFee: AmountString;
|
depositFee: AmountString;
|
||||||
merchantPub: string;
|
merchantPub: string;
|
||||||
coinSig: string;
|
coinSig: string;
|
||||||
|
@ -44,7 +44,7 @@ import {
|
|||||||
hashWire,
|
hashWire,
|
||||||
Logger,
|
Logger,
|
||||||
parsePaytoUri,
|
parsePaytoUri,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
UnblindedSignature,
|
UnblindedSignature,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { DenominationRecord } from "./db.js";
|
import { DenominationRecord } from "./db.js";
|
||||||
@ -222,10 +222,11 @@ export async function depositCoin(args: {
|
|||||||
const depositPayto =
|
const depositPayto =
|
||||||
args.depositPayto ?? "payto://x-taler-bank/localhost/foo";
|
args.depositPayto ?? "payto://x-taler-bank/localhost/foo";
|
||||||
const wireSalt = encodeCrock(getRandomBytes(16));
|
const wireSalt = encodeCrock(getRandomBytes(16));
|
||||||
|
const timestampNow = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
const contractTermsHash = encodeCrock(getRandomBytes(64));
|
const contractTermsHash = encodeCrock(getRandomBytes(64));
|
||||||
const depositTimestamp = Timestamp.truncateToSecond(Timestamp.now());
|
const depositTimestamp = timestampNow;
|
||||||
const refundDeadline = Timestamp.truncateToSecond(Timestamp.now());
|
const refundDeadline = timestampNow;
|
||||||
const wireTransferDeadline = Timestamp.truncateToSecond(Timestamp.now());
|
const wireTransferDeadline = timestampNow;
|
||||||
const merchantPub = encodeCrock(getRandomBytes(32));
|
const merchantPub = encodeCrock(getRandomBytes(32));
|
||||||
const dp = await cryptoApi.signDepositPermission({
|
const dp = await cryptoApi.signDepositPermission({
|
||||||
coinPriv: coin.coinPriv,
|
coinPriv: coin.coinPriv,
|
||||||
|
@ -49,14 +49,13 @@ import {
|
|||||||
BackupWithdrawalGroup,
|
BackupWithdrawalGroup,
|
||||||
canonicalizeBaseUrl,
|
canonicalizeBaseUrl,
|
||||||
canonicalJson,
|
canonicalJson,
|
||||||
getTimestampNow,
|
|
||||||
Logger,
|
Logger,
|
||||||
timestampToIsoString,
|
|
||||||
WalletBackupContentV1,
|
WalletBackupContentV1,
|
||||||
hash,
|
hash,
|
||||||
encodeCrock,
|
encodeCrock,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
|
AbsoluteTime,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../../common.js";
|
import { InternalWalletState } from "../../common.js";
|
||||||
import {
|
import {
|
||||||
@ -455,7 +454,7 @@ export async function exportBackup(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const ts = getTimestampNow();
|
const ts = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
|
|
||||||
if (!bs.lastBackupTimestamp) {
|
if (!bs.lastBackupTimestamp) {
|
||||||
bs.lastBackupTimestamp = ts;
|
bs.lastBackupTimestamp = ts;
|
||||||
@ -496,9 +495,9 @@ export async function exportBackup(
|
|||||||
);
|
);
|
||||||
bs.lastBackupNonce = encodeCrock(getRandomBytes(32));
|
bs.lastBackupNonce = encodeCrock(getRandomBytes(32));
|
||||||
logger.trace(
|
logger.trace(
|
||||||
`setting timestamp to ${timestampToIsoString(ts)} and nonce to ${
|
`setting timestamp to ${AbsoluteTime.toIsoString(
|
||||||
bs.lastBackupNonce
|
AbsoluteTime.fromTimestamp(ts),
|
||||||
}`,
|
)} and nonce to ${bs.lastBackupNonce}`,
|
||||||
);
|
);
|
||||||
await tx.config.put({
|
await tx.config.put({
|
||||||
key: WALLET_BACKUP_STATE_KEY,
|
key: WALLET_BACKUP_STATE_KEY,
|
||||||
|
@ -20,7 +20,6 @@ import {
|
|||||||
Amounts,
|
Amounts,
|
||||||
BackupDenomSel,
|
BackupDenomSel,
|
||||||
WalletBackupContentV1,
|
WalletBackupContentV1,
|
||||||
getTimestampNow,
|
|
||||||
BackupCoinSourceType,
|
BackupCoinSourceType,
|
||||||
BackupProposalStatus,
|
BackupProposalStatus,
|
||||||
codecForContractTerms,
|
codecForContractTerms,
|
||||||
@ -28,6 +27,8 @@ import {
|
|||||||
RefreshReason,
|
RefreshReason,
|
||||||
BackupRefreshReason,
|
BackupRefreshReason,
|
||||||
DenomKeyType,
|
DenomKeyType,
|
||||||
|
AbsoluteTime,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
WalletContractData,
|
WalletContractData,
|
||||||
@ -277,8 +278,8 @@ export async function importBackup(
|
|||||||
permanent: true,
|
permanent: true,
|
||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
lastUpdate: undefined,
|
lastUpdate: undefined,
|
||||||
nextUpdate: getTimestampNow(),
|
nextUpdate: TalerProtocolTimestamp.now(),
|
||||||
nextRefreshCheck: getTimestampNow(),
|
nextRefreshCheck: TalerProtocolTimestamp.now(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +466,6 @@ export async function importBackup(
|
|||||||
senderWire: backupReserve.sender_wire,
|
senderWire: backupReserve.sender_wire,
|
||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
lastSuccessfulStatusQuery: { t_ms: "never" },
|
|
||||||
initialWithdrawalGroupId:
|
initialWithdrawalGroupId:
|
||||||
backupReserve.initial_withdrawal_group_id,
|
backupReserve.initial_withdrawal_group_id,
|
||||||
initialWithdrawalStarted:
|
initialWithdrawalStarted:
|
||||||
@ -752,7 +752,7 @@ export async function importBackup(
|
|||||||
noncePub:
|
noncePub:
|
||||||
cryptoComp.proposalNoncePrivToPub[backupPurchase.nonce_priv],
|
cryptoComp.proposalNoncePrivToPub[backupPurchase.nonce_priv],
|
||||||
lastPayError: undefined,
|
lastPayError: undefined,
|
||||||
autoRefundDeadline: { t_ms: "never" },
|
autoRefundDeadline: TalerProtocolTimestamp.never(),
|
||||||
refundStatusRetryInfo: initRetryInfo(),
|
refundStatusRetryInfo: initRetryInfo(),
|
||||||
lastRefundStatusError: undefined,
|
lastRefundStatusError: undefined,
|
||||||
timestampAccept: backupPurchase.timestamp_accept,
|
timestampAccept: backupPurchase.timestamp_accept,
|
||||||
|
@ -40,21 +40,19 @@ import {
|
|||||||
ConfirmPayResultType,
|
ConfirmPayResultType,
|
||||||
DenomKeyType,
|
DenomKeyType,
|
||||||
durationFromSpec,
|
durationFromSpec,
|
||||||
getTimestampNow,
|
|
||||||
hashDenomPub,
|
hashDenomPub,
|
||||||
HttpStatusCode,
|
HttpStatusCode,
|
||||||
j2s,
|
j2s,
|
||||||
LibtoolVersion,
|
|
||||||
Logger,
|
Logger,
|
||||||
notEmpty,
|
notEmpty,
|
||||||
PreparePayResultType,
|
PreparePayResultType,
|
||||||
RecoveryLoadRequest,
|
RecoveryLoadRequest,
|
||||||
RecoveryMergeStrategy,
|
RecoveryMergeStrategy,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
timestampAddDuration,
|
|
||||||
URL,
|
URL,
|
||||||
WalletBackupContentV1,
|
WalletBackupContentV1,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { gunzipSync, gzipSync } from "fflate";
|
import { gunzipSync, gzipSync } from "fflate";
|
||||||
import { InternalWalletState } from "../../common.js";
|
import { InternalWalletState } from "../../common.js";
|
||||||
@ -250,11 +248,13 @@ interface BackupForProviderArgs {
|
|||||||
retryAfterPayment: boolean;
|
retryAfterPayment: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNextBackupTimestamp(): Timestamp {
|
function getNextBackupTimestamp(): TalerProtocolTimestamp {
|
||||||
// FIXME: Randomize!
|
// FIXME: Randomize!
|
||||||
return timestampAddDuration(
|
return AbsoluteTime.toTimestamp(
|
||||||
getTimestampNow(),
|
AbsoluteTime.addDuration(
|
||||||
durationFromSpec({ minutes: 5 }),
|
AbsoluteTime.now(),
|
||||||
|
durationFromSpec({ minutes: 5 }),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ async function runBackupCycleForProvider(
|
|||||||
if (!prov) {
|
if (!prov) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
prov.lastBackupCycleTimestamp = getTimestampNow();
|
prov.lastBackupCycleTimestamp = TalerProtocolTimestamp.now();
|
||||||
prov.state = {
|
prov.state = {
|
||||||
tag: BackupProviderStateTag.Ready,
|
tag: BackupProviderStateTag.Ready,
|
||||||
nextBackupTimestamp: getNextBackupTimestamp(),
|
nextBackupTimestamp: getNextBackupTimestamp(),
|
||||||
@ -404,7 +404,7 @@ async function runBackupCycleForProvider(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
prov.lastBackupHash = encodeCrock(currentBackupHash);
|
prov.lastBackupHash = encodeCrock(currentBackupHash);
|
||||||
prov.lastBackupCycleTimestamp = getTimestampNow();
|
prov.lastBackupCycleTimestamp = TalerProtocolTimestamp.now();
|
||||||
prov.state = {
|
prov.state = {
|
||||||
tag: BackupProviderStateTag.Ready,
|
tag: BackupProviderStateTag.Ready,
|
||||||
nextBackupTimestamp: getNextBackupTimestamp(),
|
nextBackupTimestamp: getNextBackupTimestamp(),
|
||||||
@ -641,7 +641,7 @@ export async function addBackupProvider(
|
|||||||
if (req.activate) {
|
if (req.activate) {
|
||||||
oldProv.state = {
|
oldProv.state = {
|
||||||
tag: BackupProviderStateTag.Ready,
|
tag: BackupProviderStateTag.Ready,
|
||||||
nextBackupTimestamp: getTimestampNow(),
|
nextBackupTimestamp: TalerProtocolTimestamp.now(),
|
||||||
};
|
};
|
||||||
logger.info("setting existing backup provider to active");
|
logger.info("setting existing backup provider to active");
|
||||||
await tx.backupProviders.put(oldProv);
|
await tx.backupProviders.put(oldProv);
|
||||||
@ -662,7 +662,7 @@ export async function addBackupProvider(
|
|||||||
if (req.activate) {
|
if (req.activate) {
|
||||||
state = {
|
state = {
|
||||||
tag: BackupProviderStateTag.Ready,
|
tag: BackupProviderStateTag.Ready,
|
||||||
nextBackupTimestamp: getTimestampNow(),
|
nextBackupTimestamp: TalerProtocolTimestamp.now(),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
state = {
|
state = {
|
||||||
@ -701,8 +701,8 @@ export interface ProviderInfo {
|
|||||||
* Last communication issue with the provider.
|
* Last communication issue with the provider.
|
||||||
*/
|
*/
|
||||||
lastError?: TalerErrorDetails;
|
lastError?: TalerErrorDetails;
|
||||||
lastSuccessfulBackupTimestamp?: Timestamp;
|
lastSuccessfulBackupTimestamp?: TalerProtocolTimestamp;
|
||||||
lastAttemptedBackupTimestamp?: Timestamp;
|
lastAttemptedBackupTimestamp?: TalerProtocolTimestamp;
|
||||||
paymentProposalIds: string[];
|
paymentProposalIds: string[];
|
||||||
backupProblem?: BackupProblem;
|
backupProblem?: BackupProblem;
|
||||||
paymentStatus: ProviderPaymentStatus;
|
paymentStatus: ProviderPaymentStatus;
|
||||||
@ -724,7 +724,7 @@ export interface BackupConflictingDeviceProblem {
|
|||||||
type: "backup-conflicting-device";
|
type: "backup-conflicting-device";
|
||||||
otherDeviceId: string;
|
otherDeviceId: string;
|
||||||
myDeviceId: string;
|
myDeviceId: string;
|
||||||
backupTimestamp: Timestamp;
|
backupTimestamp: AbsoluteTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProviderPaymentStatus =
|
export type ProviderPaymentStatus =
|
||||||
@ -774,12 +774,12 @@ export interface ProviderPaymentPending {
|
|||||||
|
|
||||||
export interface ProviderPaymentPaid {
|
export interface ProviderPaymentPaid {
|
||||||
type: ProviderPaymentType.Paid;
|
type: ProviderPaymentType.Paid;
|
||||||
paidUntil: Timestamp;
|
paidUntil: AbsoluteTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProviderPaymentTermsChanged {
|
export interface ProviderPaymentTermsChanged {
|
||||||
type: ProviderPaymentType.TermsChanged;
|
type: ProviderPaymentType.TermsChanged;
|
||||||
paidUntil: Timestamp;
|
paidUntil: AbsoluteTime;
|
||||||
oldTerms: BackupProviderTerms;
|
oldTerms: BackupProviderTerms;
|
||||||
newTerms: BackupProviderTerms;
|
newTerms: BackupProviderTerms;
|
||||||
}
|
}
|
||||||
@ -811,8 +811,8 @@ async function getProviderPaymentInfo(
|
|||||||
if (status.paid) {
|
if (status.paid) {
|
||||||
return {
|
return {
|
||||||
type: ProviderPaymentType.Paid,
|
type: ProviderPaymentType.Paid,
|
||||||
paidUntil: timestampAddDuration(
|
paidUntil: AbsoluteTime.addDuration(
|
||||||
status.contractTerms.timestamp,
|
AbsoluteTime.fromTimestamp(status.contractTerms.timestamp),
|
||||||
durationFromSpec({ years: 1 }),
|
durationFromSpec({ years: 1 }),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@ -915,7 +915,7 @@ async function backupRecoveryTheirs(
|
|||||||
paymentProposalIds: [],
|
paymentProposalIds: [],
|
||||||
state: {
|
state: {
|
||||||
tag: BackupProviderStateTag.Ready,
|
tag: BackupProviderStateTag.Ready,
|
||||||
nextBackupTimestamp: getTimestampNow(),
|
nextBackupTimestamp: TalerProtocolTimestamp.now(),
|
||||||
},
|
},
|
||||||
uids: [encodeCrock(getRandomBytes(32))],
|
uids: [encodeCrock(getRandomBytes(32))],
|
||||||
});
|
});
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
AmountJson,
|
AmountJson,
|
||||||
Amounts,
|
Amounts,
|
||||||
buildCodecForObject,
|
buildCodecForObject,
|
||||||
@ -27,21 +28,16 @@ import {
|
|||||||
ContractTerms,
|
ContractTerms,
|
||||||
CreateDepositGroupRequest,
|
CreateDepositGroupRequest,
|
||||||
CreateDepositGroupResponse,
|
CreateDepositGroupResponse,
|
||||||
DenomKeyType,
|
|
||||||
durationFromSpec,
|
durationFromSpec,
|
||||||
encodeCrock,
|
encodeCrock,
|
||||||
GetFeeForDepositRequest,
|
GetFeeForDepositRequest,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
getTimestampNow,
|
|
||||||
hashWire,
|
hashWire,
|
||||||
Logger,
|
Logger,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
parsePaytoUri,
|
parsePaytoUri,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Timestamp,
|
TalerProtocolTimestamp,
|
||||||
timestampAddDuration,
|
|
||||||
timestampIsBetween,
|
|
||||||
timestampTruncateToSecond,
|
|
||||||
TrackDepositGroupRequest,
|
TrackDepositGroupRequest,
|
||||||
TrackDepositGroupResponse,
|
TrackDepositGroupResponse,
|
||||||
URL,
|
URL,
|
||||||
@ -212,7 +208,7 @@ async function processDepositGroupImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allDeposited) {
|
if (allDeposited) {
|
||||||
dg.timestampFinished = getTimestampNow();
|
dg.timestampFinished = TalerProtocolTimestamp.now();
|
||||||
dg.operationStatus = OperationStatus.Finished;
|
dg.operationStatus = OperationStatus.Finished;
|
||||||
delete dg.lastError;
|
delete dg.lastError;
|
||||||
delete dg.retryInfo;
|
delete dg.retryInfo;
|
||||||
@ -310,13 +306,8 @@ export async function getFeeForDeposit(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const timestamp = getTimestampNow();
|
const timestamp = AbsoluteTime.now();
|
||||||
const timestampRound = timestampTruncateToSecond(timestamp);
|
const timestampRound = AbsoluteTime.toTimestamp(timestamp);
|
||||||
// const noncePair = await ws.cryptoApi.createEddsaKeypair();
|
|
||||||
// const merchantPair = await ws.cryptoApi.createEddsaKeypair();
|
|
||||||
// const wireSalt = encodeCrock(getRandomBytes(16));
|
|
||||||
// const wireHash = hashWire(req.depositPaytoUri, wireSalt);
|
|
||||||
// const wireHashLegacy = hashWireLegacy(req.depositPaytoUri, wireSalt);
|
|
||||||
const contractTerms: ContractTerms = {
|
const contractTerms: ContractTerms = {
|
||||||
auditors: [],
|
auditors: [],
|
||||||
exchanges: exchangeInfos,
|
exchanges: exchangeInfos,
|
||||||
@ -331,15 +322,14 @@ export async function getFeeForDeposit(
|
|||||||
wire_transfer_deadline: timestampRound,
|
wire_transfer_deadline: timestampRound,
|
||||||
order_id: "",
|
order_id: "",
|
||||||
h_wire: "",
|
h_wire: "",
|
||||||
pay_deadline: timestampAddDuration(
|
pay_deadline: AbsoluteTime.toTimestamp(
|
||||||
timestampRound,
|
AbsoluteTime.addDuration(timestamp, durationFromSpec({ hours: 1 })),
|
||||||
durationFromSpec({ hours: 1 }),
|
|
||||||
),
|
),
|
||||||
merchant: {
|
merchant: {
|
||||||
name: "",
|
name: "",
|
||||||
},
|
},
|
||||||
merchant_pub: "",
|
merchant_pub: "",
|
||||||
refund_deadline: { t_ms: 0 },
|
refund_deadline: TalerProtocolTimestamp.zero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const contractData = extractContractData(contractTerms, "", "");
|
const contractData = extractContractData(contractTerms, "", "");
|
||||||
@ -399,8 +389,8 @@ export async function createDepositGroup(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const timestamp = getTimestampNow();
|
const now = AbsoluteTime.now();
|
||||||
const timestampRound = timestampTruncateToSecond(timestamp);
|
const nowRounded = AbsoluteTime.toTimestamp(now);
|
||||||
const noncePair = await ws.cryptoApi.createEddsaKeypair();
|
const noncePair = await ws.cryptoApi.createEddsaKeypair();
|
||||||
const merchantPair = await ws.cryptoApi.createEddsaKeypair();
|
const merchantPair = await ws.cryptoApi.createEddsaKeypair();
|
||||||
const wireSalt = encodeCrock(getRandomBytes(16));
|
const wireSalt = encodeCrock(getRandomBytes(16));
|
||||||
@ -412,24 +402,23 @@ export async function createDepositGroup(
|
|||||||
max_fee: Amounts.stringify(amount),
|
max_fee: Amounts.stringify(amount),
|
||||||
max_wire_fee: Amounts.stringify(amount),
|
max_wire_fee: Amounts.stringify(amount),
|
||||||
wire_method: p.targetType,
|
wire_method: p.targetType,
|
||||||
timestamp: timestampRound,
|
timestamp: nowRounded,
|
||||||
merchant_base_url: "",
|
merchant_base_url: "",
|
||||||
summary: "",
|
summary: "",
|
||||||
nonce: noncePair.pub,
|
nonce: noncePair.pub,
|
||||||
wire_transfer_deadline: timestampRound,
|
wire_transfer_deadline: nowRounded,
|
||||||
order_id: "",
|
order_id: "",
|
||||||
// This is always the v2 wire hash, as we're the "merchant" and support v2.
|
// This is always the v2 wire hash, as we're the "merchant" and support v2.
|
||||||
h_wire: wireHash,
|
h_wire: wireHash,
|
||||||
// Required for older exchanges.
|
// Required for older exchanges.
|
||||||
pay_deadline: timestampAddDuration(
|
pay_deadline: AbsoluteTime.toTimestamp(
|
||||||
timestampRound,
|
AbsoluteTime.addDuration(now, durationFromSpec({ hours: 1 })),
|
||||||
durationFromSpec({ hours: 1 }),
|
|
||||||
),
|
),
|
||||||
merchant: {
|
merchant: {
|
||||||
name: "",
|
name: "",
|
||||||
},
|
},
|
||||||
merchant_pub: merchantPair.pub,
|
merchant_pub: merchantPair.pub,
|
||||||
refund_deadline: { t_ms: 0 },
|
refund_deadline: TalerProtocolTimestamp.zero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const contractTermsHash = await ws.cryptoApi.hashString(
|
const contractTermsHash = await ws.cryptoApi.hashString(
|
||||||
@ -482,7 +471,7 @@ export async function createDepositGroup(
|
|||||||
depositGroupId,
|
depositGroupId,
|
||||||
noncePriv: noncePair.priv,
|
noncePriv: noncePair.priv,
|
||||||
noncePub: noncePair.pub,
|
noncePub: noncePair.pub,
|
||||||
timestampCreated: timestamp,
|
timestampCreated: AbsoluteTime.toTimestamp(now),
|
||||||
timestampFinished: undefined,
|
timestampFinished: undefined,
|
||||||
payCoinSelection: payCoinSel,
|
payCoinSelection: payCoinSel,
|
||||||
payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
|
payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
|
||||||
@ -570,10 +559,10 @@ export async function getEffectiveDepositAmount(
|
|||||||
// about "find method not found on undefined" when the wireType
|
// about "find method not found on undefined" when the wireType
|
||||||
// is not supported by the Exchange.
|
// is not supported by the Exchange.
|
||||||
const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
|
const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
|
||||||
return timestampIsBetween(
|
return AbsoluteTime.isBetween(
|
||||||
getTimestampNow(),
|
AbsoluteTime.now(),
|
||||||
x.startStamp,
|
AbsoluteTime.fromTimestamp(x.startStamp),
|
||||||
x.endStamp,
|
AbsoluteTime.fromTimestamp(x.endStamp),
|
||||||
);
|
);
|
||||||
})?.wireFee;
|
})?.wireFee;
|
||||||
if (fee) {
|
if (fee) {
|
||||||
@ -656,10 +645,10 @@ export async function getTotalFeeForDepositAmount(
|
|||||||
// about "find method not found on undefined" when the wireType
|
// about "find method not found on undefined" when the wireType
|
||||||
// is not supported by the Exchange.
|
// is not supported by the Exchange.
|
||||||
const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
|
const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
|
||||||
return timestampIsBetween(
|
return AbsoluteTime.isBetween(
|
||||||
getTimestampNow(),
|
AbsoluteTime.now(),
|
||||||
x.startStamp,
|
AbsoluteTime.fromTimestamp(x.startStamp),
|
||||||
x.endStamp,
|
AbsoluteTime.fromTimestamp(x.endStamp),
|
||||||
);
|
);
|
||||||
})?.wireFee;
|
})?.wireFee;
|
||||||
if (fee) {
|
if (fee) {
|
||||||
|
@ -28,8 +28,6 @@ import {
|
|||||||
durationFromSpec,
|
durationFromSpec,
|
||||||
ExchangeSignKeyJson,
|
ExchangeSignKeyJson,
|
||||||
ExchangeWireJson,
|
ExchangeWireJson,
|
||||||
getTimestampNow,
|
|
||||||
isTimestampExpired,
|
|
||||||
Logger,
|
Logger,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
parsePaytoUri,
|
parsePaytoUri,
|
||||||
@ -37,13 +35,15 @@ import {
|
|||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
URL,
|
URL,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
hashDenomPub,
|
hashDenomPub,
|
||||||
LibtoolVersion,
|
LibtoolVersion,
|
||||||
codecForAny,
|
codecForAny,
|
||||||
DenominationPubKey,
|
DenominationPubKey,
|
||||||
DenomKeyType,
|
DenomKeyType,
|
||||||
ExchangeKeysJson,
|
ExchangeKeysJson,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
|
TalerProtocolDuration,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util";
|
import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util";
|
||||||
import { CryptoApi } from "../crypto/workers/cryptoApi.js";
|
import { CryptoApi } from "../crypto/workers/cryptoApi.js";
|
||||||
@ -57,7 +57,7 @@ import {
|
|||||||
WireInfo,
|
WireInfo,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import {
|
import {
|
||||||
getExpiryTimestamp,
|
getExpiry,
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
readSuccessResponseTextOrThrow,
|
readSuccessResponseTextOrThrow,
|
||||||
@ -80,7 +80,7 @@ const logger = new Logger("exchanges.ts");
|
|||||||
function denominationRecordFromKeys(
|
function denominationRecordFromKeys(
|
||||||
exchangeBaseUrl: string,
|
exchangeBaseUrl: string,
|
||||||
exchangeMasterPub: string,
|
exchangeMasterPub: string,
|
||||||
listIssueDate: Timestamp,
|
listIssueDate: TalerProtocolTimestamp,
|
||||||
denomIn: ExchangeDenomination,
|
denomIn: ExchangeDenomination,
|
||||||
): DenominationRecord {
|
): DenominationRecord {
|
||||||
let denomPub: DenominationPubKey;
|
let denomPub: DenominationPubKey;
|
||||||
@ -132,7 +132,9 @@ async function handleExchangeUpdateError(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getExchangeRequestTimeout(): Duration {
|
export function getExchangeRequestTimeout(): Duration {
|
||||||
return { d_ms: 5000 };
|
return Duration.fromSpec({
|
||||||
|
seconds: 5,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExchangeTosDownloadResult {
|
export interface ExchangeTosDownloadResult {
|
||||||
@ -362,12 +364,11 @@ export async function updateExchangeFromUrl(
|
|||||||
async function provideExchangeRecord(
|
async function provideExchangeRecord(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
exchange: ExchangeRecord;
|
exchange: ExchangeRecord;
|
||||||
exchangeDetails: ExchangeDetailsRecord | undefined;
|
exchangeDetails: ExchangeDetailsRecord | undefined;
|
||||||
}> {
|
}> {
|
||||||
|
|
||||||
return await ws.db
|
return await ws.db
|
||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
exchanges: x.exchanges,
|
exchanges: x.exchanges,
|
||||||
@ -376,14 +377,14 @@ async function provideExchangeRecord(
|
|||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
let exchange = await tx.exchanges.get(baseUrl);
|
let exchange = await tx.exchanges.get(baseUrl);
|
||||||
if (!exchange) {
|
if (!exchange) {
|
||||||
const r = {
|
const r: ExchangeRecord = {
|
||||||
permanent: true,
|
permanent: true,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
detailsPointer: undefined,
|
detailsPointer: undefined,
|
||||||
lastUpdate: undefined,
|
lastUpdate: undefined,
|
||||||
nextUpdate: now,
|
nextUpdate: AbsoluteTime.toTimestamp(now),
|
||||||
nextRefreshCheck: now,
|
nextRefreshCheck: AbsoluteTime.toTimestamp(now),
|
||||||
};
|
};
|
||||||
await tx.exchanges.put(r);
|
await tx.exchanges.put(r);
|
||||||
exchange = r;
|
exchange = r;
|
||||||
@ -400,10 +401,10 @@ interface ExchangeKeysDownloadResult {
|
|||||||
currentDenominations: DenominationRecord[];
|
currentDenominations: DenominationRecord[];
|
||||||
protocolVersion: string;
|
protocolVersion: string;
|
||||||
signingKeys: ExchangeSignKeyJson[];
|
signingKeys: ExchangeSignKeyJson[];
|
||||||
reserveClosingDelay: Duration;
|
reserveClosingDelay: TalerProtocolDuration;
|
||||||
expiry: Timestamp;
|
expiry: TalerProtocolTimestamp;
|
||||||
recoup: Recoup[];
|
recoup: Recoup[];
|
||||||
listIssueDate: Timestamp;
|
listIssueDate: TalerProtocolTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -475,9 +476,11 @@ async function downloadExchangeKeysInfo(
|
|||||||
protocolVersion: exchangeKeysJsonUnchecked.version,
|
protocolVersion: exchangeKeysJsonUnchecked.version,
|
||||||
signingKeys: exchangeKeysJsonUnchecked.signkeys,
|
signingKeys: exchangeKeysJsonUnchecked.signkeys,
|
||||||
reserveClosingDelay: exchangeKeysJsonUnchecked.reserve_closing_delay,
|
reserveClosingDelay: exchangeKeysJsonUnchecked.reserve_closing_delay,
|
||||||
expiry: getExpiryTimestamp(resp, {
|
expiry: AbsoluteTime.toTimestamp(
|
||||||
minDuration: durationFromSpec({ hours: 1 }),
|
getExpiry(resp, {
|
||||||
}),
|
minDuration: durationFromSpec({ hours: 1 }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
recoup: exchangeKeysJsonUnchecked.recoup ?? [],
|
recoup: exchangeKeysJsonUnchecked.recoup ?? [],
|
||||||
listIssueDate: exchangeKeysJsonUnchecked.list_issue_date,
|
listIssueDate: exchangeKeysJsonUnchecked.list_issue_date,
|
||||||
};
|
};
|
||||||
@ -529,12 +532,20 @@ async function updateExchangeFromUrlImpl(
|
|||||||
exchangeDetails: ExchangeDetailsRecord;
|
exchangeDetails: ExchangeDetailsRecord;
|
||||||
}> {
|
}> {
|
||||||
logger.info(`updating exchange info for ${baseUrl}, forced: ${forceNow}`);
|
logger.info(`updating exchange info for ${baseUrl}, forced: ${forceNow}`);
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.now();
|
||||||
baseUrl = canonicalizeBaseUrl(baseUrl);
|
baseUrl = canonicalizeBaseUrl(baseUrl);
|
||||||
|
|
||||||
const { exchange, exchangeDetails } = await provideExchangeRecord(ws, baseUrl, now);
|
const { exchange, exchangeDetails } = await provideExchangeRecord(
|
||||||
|
ws,
|
||||||
|
baseUrl,
|
||||||
|
now,
|
||||||
|
);
|
||||||
|
|
||||||
if (!forceNow && exchangeDetails !== undefined && !isTimestampExpired(exchange.nextUpdate)) {
|
if (
|
||||||
|
!forceNow &&
|
||||||
|
exchangeDetails !== undefined &&
|
||||||
|
!AbsoluteTime.isExpired(AbsoluteTime.fromTimestamp(exchange.nextUpdate))
|
||||||
|
) {
|
||||||
logger.info("using existing exchange info");
|
logger.info("using existing exchange info");
|
||||||
return { exchange, exchangeDetails };
|
return { exchange, exchangeDetails };
|
||||||
}
|
}
|
||||||
@ -575,7 +586,8 @@ async function updateExchangeFromUrlImpl(
|
|||||||
timeout,
|
timeout,
|
||||||
acceptedFormat,
|
acceptedFormat,
|
||||||
);
|
);
|
||||||
const tosHasBeenAccepted = exchangeDetails?.termsOfServiceAcceptedEtag === tosDownload.tosEtag
|
const tosHasBeenAccepted =
|
||||||
|
exchangeDetails?.termsOfServiceAcceptedEtag === tosDownload.tosEtag;
|
||||||
|
|
||||||
let recoupGroupId: string | undefined;
|
let recoupGroupId: string | undefined;
|
||||||
|
|
||||||
@ -611,23 +623,25 @@ async function updateExchangeFromUrlImpl(
|
|||||||
exchangeBaseUrl: r.baseUrl,
|
exchangeBaseUrl: r.baseUrl,
|
||||||
wireInfo,
|
wireInfo,
|
||||||
termsOfServiceText: tosDownload.tosText,
|
termsOfServiceText: tosDownload.tosText,
|
||||||
termsOfServiceAcceptedEtag: tosHasBeenAccepted ? tosDownload.tosEtag : undefined,
|
termsOfServiceAcceptedEtag: tosHasBeenAccepted
|
||||||
|
? tosDownload.tosEtag
|
||||||
|
: undefined,
|
||||||
termsOfServiceContentType: tosDownload.tosContentType,
|
termsOfServiceContentType: tosDownload.tosContentType,
|
||||||
termsOfServiceLastEtag: tosDownload.tosEtag,
|
termsOfServiceLastEtag: tosDownload.tosEtag,
|
||||||
termsOfServiceAcceptedTimestamp: getTimestampNow(),
|
termsOfServiceAcceptedTimestamp: TalerProtocolTimestamp.now(),
|
||||||
};
|
};
|
||||||
// FIXME: only update if pointer got updated
|
// FIXME: only update if pointer got updated
|
||||||
r.lastError = undefined;
|
r.lastError = undefined;
|
||||||
r.retryInfo = initRetryInfo();
|
r.retryInfo = initRetryInfo();
|
||||||
r.lastUpdate = getTimestampNow();
|
r.lastUpdate = TalerProtocolTimestamp.now();
|
||||||
r.nextUpdate = keysInfo.expiry;
|
r.nextUpdate = keysInfo.expiry;
|
||||||
// New denominations might be available.
|
// New denominations might be available.
|
||||||
r.nextRefreshCheck = getTimestampNow();
|
r.nextRefreshCheck = TalerProtocolTimestamp.now();
|
||||||
r.detailsPointer = {
|
r.detailsPointer = {
|
||||||
currency: details.currency,
|
currency: details.currency,
|
||||||
masterPublicKey: details.masterPublicKey,
|
masterPublicKey: details.masterPublicKey,
|
||||||
// FIXME: only change if pointer really changed
|
// FIXME: only change if pointer really changed
|
||||||
updateClock: getTimestampNow(),
|
updateClock: TalerProtocolTimestamp.now(),
|
||||||
protocolVersionRange: keysInfo.protocolVersion,
|
protocolVersionRange: keysInfo.protocolVersion,
|
||||||
};
|
};
|
||||||
await tx.exchanges.put(r);
|
await tx.exchanges.put(r);
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
import {
|
import {
|
||||||
AmountJson,
|
AmountJson,
|
||||||
Amounts,
|
Amounts,
|
||||||
CheckPaymentResponse,
|
|
||||||
codecForContractTerms,
|
codecForContractTerms,
|
||||||
codecForMerchantPayResponse,
|
codecForMerchantPayResponse,
|
||||||
codecForProposal,
|
codecForProposal,
|
||||||
@ -41,11 +40,8 @@ import {
|
|||||||
durationMin,
|
durationMin,
|
||||||
durationMul,
|
durationMul,
|
||||||
encodeCrock,
|
encodeCrock,
|
||||||
getDurationRemaining,
|
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
getTimestampNow,
|
|
||||||
HttpStatusCode,
|
HttpStatusCode,
|
||||||
isTimestampExpired,
|
|
||||||
j2s,
|
j2s,
|
||||||
kdf,
|
kdf,
|
||||||
Logger,
|
Logger,
|
||||||
@ -57,9 +53,9 @@ import {
|
|||||||
stringToBytes,
|
stringToBytes,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
timestampAddDuration,
|
|
||||||
URL,
|
URL,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { EXCHANGE_COINS_LOCK, InternalWalletState } from "../common.js";
|
import { EXCHANGE_COINS_LOCK, InternalWalletState } from "../common.js";
|
||||||
import {
|
import {
|
||||||
@ -172,7 +168,9 @@ function isSpendableCoin(coin: CoinRecord, denom: DenominationRecord): boolean {
|
|||||||
if (coin.status !== CoinStatus.Fresh) {
|
if (coin.status !== CoinStatus.Fresh) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isTimestampExpired(denom.stampExpireDeposit)) {
|
if (
|
||||||
|
AbsoluteTime.isExpired(AbsoluteTime.fromTimestamp(denom.stampExpireDeposit))
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -187,7 +185,7 @@ export interface CoinSelectionRequest {
|
|||||||
/**
|
/**
|
||||||
* Timestamp of the contract.
|
* Timestamp of the contract.
|
||||||
*/
|
*/
|
||||||
timestamp: Timestamp;
|
timestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
wireMethod: string;
|
wireMethod: string;
|
||||||
|
|
||||||
@ -422,7 +420,7 @@ async function recordConfirmPay(
|
|||||||
payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
|
payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
|
||||||
totalPayCost: payCostInfo,
|
totalPayCost: payCostInfo,
|
||||||
coinDepositPermissions,
|
coinDepositPermissions,
|
||||||
timestampAccept: getTimestampNow(),
|
timestampAccept: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
|
||||||
timestampLastRefundStatus: undefined,
|
timestampLastRefundStatus: undefined,
|
||||||
proposalId: proposal.proposalId,
|
proposalId: proposal.proposalId,
|
||||||
lastPayError: undefined,
|
lastPayError: undefined,
|
||||||
@ -784,7 +782,7 @@ async function processDownloadProposalImpl(
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = makeErrorDetails(
|
const err = makeErrorDetails(
|
||||||
TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED,
|
TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED,
|
||||||
"schema validation failed",
|
`schema validation failed: ${e}`,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
await failProposalPermanently(ws, proposalId, err);
|
await failProposalPermanently(ws, proposalId, err);
|
||||||
@ -921,7 +919,7 @@ async function startDownloadProposal(
|
|||||||
noncePriv: priv,
|
noncePriv: priv,
|
||||||
noncePub: pub,
|
noncePub: pub,
|
||||||
claimToken,
|
claimToken,
|
||||||
timestamp: getTimestampNow(),
|
timestamp: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
|
||||||
merchantBaseUrl,
|
merchantBaseUrl,
|
||||||
orderId,
|
orderId,
|
||||||
proposalId: proposalId,
|
proposalId: proposalId,
|
||||||
@ -956,7 +954,7 @@ async function storeFirstPaySuccess(
|
|||||||
sessionId: string | undefined,
|
sessionId: string | undefined,
|
||||||
paySig: string,
|
paySig: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
await ws.db
|
await ws.db
|
||||||
.mktx((x) => ({ purchases: x.purchases }))
|
.mktx((x) => ({ purchases: x.purchases }))
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
@ -978,13 +976,16 @@ async function storeFirstPaySuccess(
|
|||||||
purchase.payRetryInfo = initRetryInfo();
|
purchase.payRetryInfo = initRetryInfo();
|
||||||
purchase.merchantPaySig = paySig;
|
purchase.merchantPaySig = paySig;
|
||||||
if (isFirst) {
|
if (isFirst) {
|
||||||
const ar = purchase.download.contractData.autoRefund;
|
const protoAr = purchase.download.contractData.autoRefund;
|
||||||
if (ar) {
|
if (protoAr) {
|
||||||
|
const ar = Duration.fromTalerProtocolDuration(protoAr);
|
||||||
logger.info("auto_refund present");
|
logger.info("auto_refund present");
|
||||||
purchase.refundQueryRequested = true;
|
purchase.refundQueryRequested = true;
|
||||||
purchase.refundStatusRetryInfo = initRetryInfo();
|
purchase.refundStatusRetryInfo = initRetryInfo();
|
||||||
purchase.lastRefundStatusError = undefined;
|
purchase.lastRefundStatusError = undefined;
|
||||||
purchase.autoRefundDeadline = timestampAddDuration(now, ar);
|
purchase.autoRefundDeadline = AbsoluteTime.toTimestamp(
|
||||||
|
AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await tx.purchases.put(purchase);
|
await tx.purchases.put(purchase);
|
||||||
@ -1150,7 +1151,7 @@ async function unblockBackup(
|
|||||||
if (bp.state.tag === BackupProviderStateTag.Retrying) {
|
if (bp.state.tag === BackupProviderStateTag.Retrying) {
|
||||||
bp.state = {
|
bp.state = {
|
||||||
tag: BackupProviderStateTag.Ready,
|
tag: BackupProviderStateTag.Ready,
|
||||||
nextBackupTimestamp: getTimestampNow(),
|
nextBackupTimestamp: TalerProtocolTimestamp.now(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -35,7 +35,7 @@ import {
|
|||||||
PendingTaskType,
|
PendingTaskType,
|
||||||
ReserveType,
|
ReserveType,
|
||||||
} from "../pending-types.js";
|
} from "../pending-types.js";
|
||||||
import { getTimestampNow, Timestamp } from "@gnu-taler/taler-util";
|
import { AbsoluteTime } from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../common.js";
|
import { InternalWalletState } from "../common.js";
|
||||||
import { GetReadOnlyAccess } from "../util/query.js";
|
import { GetReadOnlyAccess } from "../util/query.js";
|
||||||
|
|
||||||
@ -44,21 +44,25 @@ async function gatherExchangePending(
|
|||||||
exchanges: typeof WalletStoresV1.exchanges;
|
exchanges: typeof WalletStoresV1.exchanges;
|
||||||
exchangeDetails: typeof WalletStoresV1.exchangeDetails;
|
exchangeDetails: typeof WalletStoresV1.exchangeDetails;
|
||||||
}>,
|
}>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.exchanges.iter().forEachAsync(async (e) => {
|
await tx.exchanges.iter().forEachAsync(async (e) => {
|
||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.ExchangeUpdate,
|
type: PendingTaskType.ExchangeUpdate,
|
||||||
givesLifeness: false,
|
givesLifeness: false,
|
||||||
timestampDue: e.lastError ? e.retryInfo.nextRetry : e.nextUpdate,
|
timestampDue: e.lastError
|
||||||
|
? e.retryInfo.nextRetry
|
||||||
|
: AbsoluteTime.fromTimestamp(e.nextUpdate),
|
||||||
exchangeBaseUrl: e.baseUrl,
|
exchangeBaseUrl: e.baseUrl,
|
||||||
lastError: e.lastError,
|
lastError: e.lastError,
|
||||||
});
|
});
|
||||||
|
|
||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.ExchangeCheckRefresh,
|
type: PendingTaskType.ExchangeCheckRefresh,
|
||||||
timestampDue: e.lastError ? e.retryInfo.nextRetry : e.nextRefreshCheck,
|
timestampDue: e.lastError
|
||||||
|
? e.retryInfo.nextRetry
|
||||||
|
: AbsoluteTime.fromTimestamp(e.nextRefreshCheck),
|
||||||
givesLifeness: false,
|
givesLifeness: false,
|
||||||
exchangeBaseUrl: e.baseUrl,
|
exchangeBaseUrl: e.baseUrl,
|
||||||
});
|
});
|
||||||
@ -67,7 +71,7 @@ async function gatherExchangePending(
|
|||||||
|
|
||||||
async function gatherReservePending(
|
async function gatherReservePending(
|
||||||
tx: GetReadOnlyAccess<{ reserves: typeof WalletStoresV1.reserves }>,
|
tx: GetReadOnlyAccess<{ reserves: typeof WalletStoresV1.reserves }>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const reserves = await tx.reserves.indexes.byStatus.getAll(
|
const reserves = await tx.reserves.indexes.byStatus.getAll(
|
||||||
@ -87,7 +91,7 @@ async function gatherReservePending(
|
|||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.Reserve,
|
type: PendingTaskType.Reserve,
|
||||||
givesLifeness: true,
|
givesLifeness: true,
|
||||||
timestampDue: reserve.retryInfo?.nextRetry ?? Timestamp.now(),
|
timestampDue: reserve.retryInfo?.nextRetry ?? AbsoluteTime.now(),
|
||||||
stage: reserve.reserveStatus,
|
stage: reserve.reserveStatus,
|
||||||
timestampCreated: reserve.timestampCreated,
|
timestampCreated: reserve.timestampCreated,
|
||||||
reserveType,
|
reserveType,
|
||||||
@ -105,7 +109,7 @@ async function gatherReservePending(
|
|||||||
|
|
||||||
async function gatherRefreshPending(
|
async function gatherRefreshPending(
|
||||||
tx: GetReadOnlyAccess<{ refreshGroups: typeof WalletStoresV1.refreshGroups }>,
|
tx: GetReadOnlyAccess<{ refreshGroups: typeof WalletStoresV1.refreshGroups }>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll(
|
const refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll(
|
||||||
@ -136,7 +140,7 @@ async function gatherWithdrawalPending(
|
|||||||
withdrawalGroups: typeof WalletStoresV1.withdrawalGroups;
|
withdrawalGroups: typeof WalletStoresV1.withdrawalGroups;
|
||||||
planchets: typeof WalletStoresV1.planchets;
|
planchets: typeof WalletStoresV1.planchets;
|
||||||
}>,
|
}>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const wsrs = await tx.withdrawalGroups.indexes.byStatus.getAll(
|
const wsrs = await tx.withdrawalGroups.indexes.byStatus.getAll(
|
||||||
@ -169,14 +173,14 @@ async function gatherWithdrawalPending(
|
|||||||
|
|
||||||
async function gatherProposalPending(
|
async function gatherProposalPending(
|
||||||
tx: GetReadOnlyAccess<{ proposals: typeof WalletStoresV1.proposals }>,
|
tx: GetReadOnlyAccess<{ proposals: typeof WalletStoresV1.proposals }>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.proposals.iter().forEach((proposal) => {
|
await tx.proposals.iter().forEach((proposal) => {
|
||||||
if (proposal.proposalStatus == ProposalStatus.Proposed) {
|
if (proposal.proposalStatus == ProposalStatus.Proposed) {
|
||||||
// Nothing to do, user needs to choose.
|
// Nothing to do, user needs to choose.
|
||||||
} else if (proposal.proposalStatus == ProposalStatus.Downloading) {
|
} else if (proposal.proposalStatus == ProposalStatus.Downloading) {
|
||||||
const timestampDue = proposal.retryInfo?.nextRetry ?? getTimestampNow();
|
const timestampDue = proposal.retryInfo?.nextRetry ?? AbsoluteTime.now();
|
||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.ProposalDownload,
|
type: PendingTaskType.ProposalDownload,
|
||||||
givesLifeness: true,
|
givesLifeness: true,
|
||||||
@ -194,7 +198,7 @@ async function gatherProposalPending(
|
|||||||
|
|
||||||
async function gatherDepositPending(
|
async function gatherDepositPending(
|
||||||
tx: GetReadOnlyAccess<{ depositGroups: typeof WalletStoresV1.depositGroups }>,
|
tx: GetReadOnlyAccess<{ depositGroups: typeof WalletStoresV1.depositGroups }>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const dgs = await tx.depositGroups.indexes.byStatus.getAll(
|
const dgs = await tx.depositGroups.indexes.byStatus.getAll(
|
||||||
@ -204,7 +208,7 @@ async function gatherDepositPending(
|
|||||||
if (dg.timestampFinished) {
|
if (dg.timestampFinished) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const timestampDue = dg.retryInfo?.nextRetry ?? getTimestampNow();
|
const timestampDue = dg.retryInfo?.nextRetry ?? AbsoluteTime.now();
|
||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.Deposit,
|
type: PendingTaskType.Deposit,
|
||||||
givesLifeness: true,
|
givesLifeness: true,
|
||||||
@ -218,7 +222,7 @@ async function gatherDepositPending(
|
|||||||
|
|
||||||
async function gatherTipPending(
|
async function gatherTipPending(
|
||||||
tx: GetReadOnlyAccess<{ tips: typeof WalletStoresV1.tips }>,
|
tx: GetReadOnlyAccess<{ tips: typeof WalletStoresV1.tips }>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.tips.iter().forEach((tip) => {
|
await tx.tips.iter().forEach((tip) => {
|
||||||
@ -240,7 +244,7 @@ async function gatherTipPending(
|
|||||||
|
|
||||||
async function gatherPurchasePending(
|
async function gatherPurchasePending(
|
||||||
tx: GetReadOnlyAccess<{ purchases: typeof WalletStoresV1.purchases }>,
|
tx: GetReadOnlyAccess<{ purchases: typeof WalletStoresV1.purchases }>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.purchases.iter().forEach((pr) => {
|
await tx.purchases.iter().forEach((pr) => {
|
||||||
@ -249,7 +253,7 @@ async function gatherPurchasePending(
|
|||||||
pr.abortStatus === AbortStatus.None &&
|
pr.abortStatus === AbortStatus.None &&
|
||||||
!pr.payFrozen
|
!pr.payFrozen
|
||||||
) {
|
) {
|
||||||
const timestampDue = pr.payRetryInfo?.nextRetry ?? getTimestampNow();
|
const timestampDue = pr.payRetryInfo?.nextRetry ?? AbsoluteTime.now();
|
||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.Pay,
|
type: PendingTaskType.Pay,
|
||||||
givesLifeness: true,
|
givesLifeness: true,
|
||||||
@ -275,7 +279,7 @@ async function gatherPurchasePending(
|
|||||||
|
|
||||||
async function gatherRecoupPending(
|
async function gatherRecoupPending(
|
||||||
tx: GetReadOnlyAccess<{ recoupGroups: typeof WalletStoresV1.recoupGroups }>,
|
tx: GetReadOnlyAccess<{ recoupGroups: typeof WalletStoresV1.recoupGroups }>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.recoupGroups.iter().forEach((rg) => {
|
await tx.recoupGroups.iter().forEach((rg) => {
|
||||||
@ -297,7 +301,7 @@ async function gatherBackupPending(
|
|||||||
tx: GetReadOnlyAccess<{
|
tx: GetReadOnlyAccess<{
|
||||||
backupProviders: typeof WalletStoresV1.backupProviders;
|
backupProviders: typeof WalletStoresV1.backupProviders;
|
||||||
}>,
|
}>,
|
||||||
now: Timestamp,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.backupProviders.iter().forEach((bp) => {
|
await tx.backupProviders.iter().forEach((bp) => {
|
||||||
@ -305,7 +309,7 @@ async function gatherBackupPending(
|
|||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.Backup,
|
type: PendingTaskType.Backup,
|
||||||
givesLifeness: false,
|
givesLifeness: false,
|
||||||
timestampDue: bp.state.nextBackupTimestamp,
|
timestampDue: AbsoluteTime.fromTimestamp(bp.state.nextBackupTimestamp),
|
||||||
backupProviderBaseUrl: bp.baseUrl,
|
backupProviderBaseUrl: bp.baseUrl,
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
});
|
});
|
||||||
@ -325,7 +329,7 @@ async function gatherBackupPending(
|
|||||||
export async function getPendingOperations(
|
export async function getPendingOperations(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
): Promise<PendingOperationsResponse> {
|
): Promise<PendingOperationsResponse> {
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.now();
|
||||||
return await ws.db
|
return await ws.db
|
||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
backupProviders: x.backupProviders,
|
backupProviders: x.backupProviders,
|
||||||
|
@ -27,11 +27,11 @@
|
|||||||
import {
|
import {
|
||||||
Amounts,
|
Amounts,
|
||||||
codecForRecoupConfirmation,
|
codecForRecoupConfirmation,
|
||||||
getTimestampNow,
|
|
||||||
j2s,
|
j2s,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
RefreshReason,
|
RefreshReason,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
|
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
@ -110,7 +110,7 @@ async function putGroupAsFinished(
|
|||||||
}
|
}
|
||||||
if (allFinished) {
|
if (allFinished) {
|
||||||
logger.info("all recoups of recoup group are finished");
|
logger.info("all recoups of recoup group are finished");
|
||||||
recoupGroup.timestampFinished = getTimestampNow();
|
recoupGroup.timestampFinished = TalerProtocolTimestamp.now();
|
||||||
recoupGroup.retryInfo = initRetryInfo();
|
recoupGroup.retryInfo = initRetryInfo();
|
||||||
recoupGroup.lastError = undefined;
|
recoupGroup.lastError = undefined;
|
||||||
if (recoupGroup.scheduleRefreshCoins.length > 0) {
|
if (recoupGroup.scheduleRefreshCoins.length > 0) {
|
||||||
@ -467,7 +467,7 @@ export async function createRecoupGroup(
|
|||||||
coinPubs: coinPubs,
|
coinPubs: coinPubs,
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
timestampFinished: undefined,
|
timestampFinished: undefined,
|
||||||
timestampStarted: getTimestampNow(),
|
timestampStarted: TalerProtocolTimestamp.now(),
|
||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
recoupFinishedPerCoin: coinPubs.map(() => false),
|
recoupFinishedPerCoin: coinPubs.map(() => false),
|
||||||
// Will be populated later
|
// Will be populated later
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
ExchangeRefreshRevealRequest,
|
ExchangeRefreshRevealRequest,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
HttpStatusCode,
|
HttpStatusCode,
|
||||||
j2s,
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
@ -42,11 +42,8 @@ import {
|
|||||||
fnutil,
|
fnutil,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
RefreshGroupId,
|
RefreshGroupId,
|
||||||
RefreshPlanchetInfo,
|
|
||||||
RefreshReason,
|
RefreshReason,
|
||||||
stringifyTimestamp,
|
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
timestampToIsoString,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { AmountJson, Amounts } from "@gnu-taler/taler-util";
|
import { AmountJson, Amounts } from "@gnu-taler/taler-util";
|
||||||
import { amountToPretty } from "@gnu-taler/taler-util";
|
import { amountToPretty } from "@gnu-taler/taler-util";
|
||||||
@ -61,12 +58,7 @@ import {
|
|||||||
Duration,
|
Duration,
|
||||||
durationFromSpec,
|
durationFromSpec,
|
||||||
durationMul,
|
durationMul,
|
||||||
getTimestampNow,
|
AbsoluteTime,
|
||||||
isTimestampExpired,
|
|
||||||
Timestamp,
|
|
||||||
timestampAddDuration,
|
|
||||||
timestampDifference,
|
|
||||||
timestampMin,
|
|
||||||
URL,
|
URL,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { guardOperationException } from "../errors.js";
|
import { guardOperationException } from "../errors.js";
|
||||||
@ -139,7 +131,7 @@ function updateGroupStatus(rg: RefreshGroupRecord): void {
|
|||||||
rg.frozen = true;
|
rg.frozen = true;
|
||||||
rg.retryInfo = initRetryInfo();
|
rg.retryInfo = initRetryInfo();
|
||||||
} else {
|
} else {
|
||||||
rg.timestampFinished = getTimestampNow();
|
rg.timestampFinished = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
rg.operationStatus = OperationStatus.Finished;
|
rg.operationStatus = OperationStatus.Finished;
|
||||||
rg.retryInfo = initRetryInfo();
|
rg.retryInfo = initRetryInfo();
|
||||||
}
|
}
|
||||||
@ -234,19 +226,6 @@ async function refreshCreateSession(
|
|||||||
availableDenoms,
|
availableDenoms,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (logger.shouldLogTrace()) {
|
|
||||||
logger.trace(`printing selected denominations for refresh`);
|
|
||||||
logger.trace(`current time: ${stringifyTimestamp(getTimestampNow())}`);
|
|
||||||
for (const denom of newCoinDenoms.selectedDenoms) {
|
|
||||||
logger.trace(`denom ${denom.denom}, count ${denom.count}`);
|
|
||||||
logger.trace(
|
|
||||||
`withdrawal expiration ${stringifyTimestamp(
|
|
||||||
denom.denom.stampExpireWithdraw,
|
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newCoinDenoms.selectedDenoms.length === 0) {
|
if (newCoinDenoms.selectedDenoms.length === 0) {
|
||||||
logger.trace(
|
logger.trace(
|
||||||
`not refreshing, available amount ${amountToPretty(
|
`not refreshing, available amount ${amountToPretty(
|
||||||
@ -306,7 +285,9 @@ async function refreshCreateSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRefreshRequestTimeout(rg: RefreshGroupRecord): Duration {
|
function getRefreshRequestTimeout(rg: RefreshGroupRecord): Duration {
|
||||||
return { d_ms: 5000 };
|
return Duration.fromSpec({
|
||||||
|
seconds: 5,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshMelt(
|
async function refreshMelt(
|
||||||
@ -949,12 +930,12 @@ export async function createRefreshGroup(
|
|||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
inputPerCoin,
|
inputPerCoin,
|
||||||
estimatedOutputPerCoin,
|
estimatedOutputPerCoin,
|
||||||
timestampCreated: getTimestampNow(),
|
timestampCreated: TalerProtocolTimestamp.now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (oldCoinPubs.length == 0) {
|
if (oldCoinPubs.length == 0) {
|
||||||
logger.warn("created refresh group with zero coins");
|
logger.warn("created refresh group with zero coins");
|
||||||
refreshGroup.timestampFinished = getTimestampNow();
|
refreshGroup.timestampFinished = TalerProtocolTimestamp.now();
|
||||||
refreshGroup.operationStatus = OperationStatus.Finished;
|
refreshGroup.operationStatus = OperationStatus.Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -974,25 +955,23 @@ export async function createRefreshGroup(
|
|||||||
/**
|
/**
|
||||||
* Timestamp after which the wallet would do the next check for an auto-refresh.
|
* Timestamp after which the wallet would do the next check for an auto-refresh.
|
||||||
*/
|
*/
|
||||||
function getAutoRefreshCheckThreshold(d: DenominationRecord): Timestamp {
|
function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
|
||||||
const delta = timestampDifference(
|
const expireWithdraw = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
|
||||||
d.stampExpireWithdraw,
|
const expireDeposit = AbsoluteTime.fromTimestamp(d.stampExpireDeposit);
|
||||||
d.stampExpireDeposit,
|
const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
|
||||||
);
|
|
||||||
const deltaDiv = durationMul(delta, 0.75);
|
const deltaDiv = durationMul(delta, 0.75);
|
||||||
return timestampAddDuration(d.stampExpireWithdraw, deltaDiv);
|
return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp after which the wallet would do an auto-refresh.
|
* Timestamp after which the wallet would do an auto-refresh.
|
||||||
*/
|
*/
|
||||||
function getAutoRefreshExecuteThreshold(d: DenominationRecord): Timestamp {
|
function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime {
|
||||||
const delta = timestampDifference(
|
const expireWithdraw = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
|
||||||
d.stampExpireWithdraw,
|
const expireDeposit = AbsoluteTime.fromTimestamp(d.stampExpireDeposit);
|
||||||
d.stampExpireDeposit,
|
const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
|
||||||
);
|
|
||||||
const deltaDiv = durationMul(delta, 0.5);
|
const deltaDiv = durationMul(delta, 0.5);
|
||||||
return timestampAddDuration(d.stampExpireWithdraw, deltaDiv);
|
return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function autoRefresh(
|
export async function autoRefresh(
|
||||||
@ -1001,8 +980,8 @@ export async function autoRefresh(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
logger.info(`doing auto-refresh check for '${exchangeBaseUrl}'`);
|
logger.info(`doing auto-refresh check for '${exchangeBaseUrl}'`);
|
||||||
await updateExchangeFromUrl(ws, exchangeBaseUrl, undefined, true);
|
await updateExchangeFromUrl(ws, exchangeBaseUrl, undefined, true);
|
||||||
let minCheckThreshold = timestampAddDuration(
|
let minCheckThreshold = AbsoluteTime.addDuration(
|
||||||
getTimestampNow(),
|
AbsoluteTime.now(),
|
||||||
durationFromSpec({ days: 1 }),
|
durationFromSpec({ days: 1 }),
|
||||||
);
|
);
|
||||||
await ws.db
|
await ws.db
|
||||||
@ -1037,11 +1016,14 @@ export async function autoRefresh(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const executeThreshold = getAutoRefreshExecuteThreshold(denom);
|
const executeThreshold = getAutoRefreshExecuteThreshold(denom);
|
||||||
if (isTimestampExpired(executeThreshold)) {
|
if (AbsoluteTime.isExpired(executeThreshold)) {
|
||||||
refreshCoins.push(coin);
|
refreshCoins.push(coin);
|
||||||
} else {
|
} else {
|
||||||
const checkThreshold = getAutoRefreshCheckThreshold(denom);
|
const checkThreshold = getAutoRefreshCheckThreshold(denom);
|
||||||
minCheckThreshold = timestampMin(minCheckThreshold, checkThreshold);
|
minCheckThreshold = AbsoluteTime.min(
|
||||||
|
minCheckThreshold,
|
||||||
|
checkThreshold,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (refreshCoins.length > 0) {
|
if (refreshCoins.length > 0) {
|
||||||
@ -1056,12 +1038,12 @@ export async function autoRefresh(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
logger.info(
|
logger.info(
|
||||||
`current wallet time: ${timestampToIsoString(getTimestampNow())}`,
|
`current wallet time: ${AbsoluteTime.toIsoString(AbsoluteTime.now())}`,
|
||||||
);
|
);
|
||||||
logger.info(
|
logger.info(
|
||||||
`next refresh check at ${timestampToIsoString(minCheckThreshold)}`,
|
`next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,
|
||||||
);
|
);
|
||||||
exchange.nextRefreshCheck = minCheckThreshold;
|
exchange.nextRefreshCheck = AbsoluteTime.toTimestamp(minCheckThreshold);
|
||||||
await tx.exchanges.put(exchange);
|
await tx.exchanges.put(exchange);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ import {
|
|||||||
codecForAbortResponse,
|
codecForAbortResponse,
|
||||||
codecForMerchantOrderRefundPickupResponse,
|
codecForMerchantOrderRefundPickupResponse,
|
||||||
CoinPublicKey,
|
CoinPublicKey,
|
||||||
getTimestampNow,
|
|
||||||
Logger,
|
Logger,
|
||||||
MerchantCoinRefundFailureStatus,
|
MerchantCoinRefundFailureStatus,
|
||||||
MerchantCoinRefundStatus,
|
MerchantCoinRefundStatus,
|
||||||
@ -43,9 +42,10 @@ import {
|
|||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
URL,
|
URL,
|
||||||
timestampAddDuration,
|
|
||||||
codecForMerchantOrderStatusPaid,
|
codecForMerchantOrderStatusPaid,
|
||||||
isTimestampExpired,
|
AbsoluteTime,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
|
Duration,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
AbortStatus,
|
AbortStatus,
|
||||||
@ -170,7 +170,7 @@ async function applySuccessfulRefund(
|
|||||||
|
|
||||||
p.refunds[refundKey] = {
|
p.refunds[refundKey] = {
|
||||||
type: RefundState.Applied,
|
type: RefundState.Applied,
|
||||||
obtainedTime: getTimestampNow(),
|
obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
|
||||||
executionTime: r.execution_time,
|
executionTime: r.execution_time,
|
||||||
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
||||||
refundFee: denom.feeRefund,
|
refundFee: denom.feeRefund,
|
||||||
@ -222,7 +222,7 @@ async function storePendingRefund(
|
|||||||
|
|
||||||
p.refunds[refundKey] = {
|
p.refunds[refundKey] = {
|
||||||
type: RefundState.Pending,
|
type: RefundState.Pending,
|
||||||
obtainedTime: getTimestampNow(),
|
obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
|
||||||
executionTime: r.execution_time,
|
executionTime: r.execution_time,
|
||||||
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
||||||
refundFee: denom.feeRefund,
|
refundFee: denom.feeRefund,
|
||||||
@ -275,7 +275,7 @@ async function storeFailedRefund(
|
|||||||
|
|
||||||
p.refunds[refundKey] = {
|
p.refunds[refundKey] = {
|
||||||
type: RefundState.Failed,
|
type: RefundState.Failed,
|
||||||
obtainedTime: getTimestampNow(),
|
obtainedTime: TalerProtocolTimestamp.now(),
|
||||||
executionTime: r.execution_time,
|
executionTime: r.execution_time,
|
||||||
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
||||||
refundFee: denom.feeRefund,
|
refundFee: denom.feeRefund,
|
||||||
@ -327,7 +327,7 @@ async function acceptRefunds(
|
|||||||
reason: RefundReason,
|
reason: RefundReason,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
logger.trace("handling refunds", refunds);
|
logger.trace("handling refunds", refunds);
|
||||||
const now = getTimestampNow();
|
const now = TalerProtocolTimestamp.now();
|
||||||
|
|
||||||
await ws.db
|
await ws.db
|
||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
@ -401,7 +401,10 @@ async function acceptRefunds(
|
|||||||
if (
|
if (
|
||||||
p.timestampFirstSuccessfulPay &&
|
p.timestampFirstSuccessfulPay &&
|
||||||
p.autoRefundDeadline &&
|
p.autoRefundDeadline &&
|
||||||
p.autoRefundDeadline.t_ms > now.t_ms
|
AbsoluteTime.cmp(
|
||||||
|
AbsoluteTime.fromTimestamp(p.autoRefundDeadline),
|
||||||
|
AbsoluteTime.fromTimestamp(now),
|
||||||
|
) > 0
|
||||||
) {
|
) {
|
||||||
queryDone = false;
|
queryDone = false;
|
||||||
}
|
}
|
||||||
@ -556,8 +559,10 @@ export async function applyRefund(
|
|||||||
).amount,
|
).amount,
|
||||||
).amount;
|
).amount;
|
||||||
} else {
|
} else {
|
||||||
amountRefundGone = Amounts.add(amountRefundGone, refund.refundAmount)
|
amountRefundGone = Amounts.add(
|
||||||
.amount;
|
amountRefundGone,
|
||||||
|
refund.refundAmount,
|
||||||
|
).amount;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -623,7 +628,9 @@ async function processPurchaseQueryRefundImpl(
|
|||||||
if (
|
if (
|
||||||
waitForAutoRefund &&
|
waitForAutoRefund &&
|
||||||
purchase.autoRefundDeadline &&
|
purchase.autoRefundDeadline &&
|
||||||
!isTimestampExpired(purchase.autoRefundDeadline)
|
!AbsoluteTime.isExpired(
|
||||||
|
AbsoluteTime.fromTimestamp(purchase.autoRefundDeadline),
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
const requestUrl = new URL(
|
const requestUrl = new URL(
|
||||||
`orders/${purchase.download.contractData.orderId}`,
|
`orders/${purchase.download.contractData.orderId}`,
|
||||||
@ -731,11 +738,13 @@ async function processPurchaseQueryRefundImpl(
|
|||||||
purchase.payCoinSelection.coinContributions[i],
|
purchase.payCoinSelection.coinContributions[i],
|
||||||
),
|
),
|
||||||
rtransaction_id: 0,
|
rtransaction_id: 0,
|
||||||
execution_time: timestampAddDuration(
|
execution_time: AbsoluteTime.toTimestamp(
|
||||||
purchase.download.contractData.timestamp,
|
AbsoluteTime.addDuration(
|
||||||
{
|
AbsoluteTime.fromTimestamp(
|
||||||
d_ms: 1000,
|
purchase.download.contractData.timestamp,
|
||||||
},
|
),
|
||||||
|
Duration.fromSpec({ seconds: 1 }),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -29,15 +29,13 @@ import {
|
|||||||
durationMin,
|
durationMin,
|
||||||
encodeCrock,
|
encodeCrock,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
getTimestampNow,
|
|
||||||
j2s,
|
j2s,
|
||||||
Logger,
|
Logger,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
randomBytes,
|
randomBytes,
|
||||||
ReserveTransactionType,
|
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
URL,
|
URL,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../common.js";
|
import { InternalWalletState } from "../common.js";
|
||||||
@ -172,7 +170,7 @@ export async function createReserve(
|
|||||||
req: CreateReserveRequest,
|
req: CreateReserveRequest,
|
||||||
): Promise<CreateReserveResponse> {
|
): Promise<CreateReserveResponse> {
|
||||||
const keypair = await ws.cryptoApi.createEddsaKeypair();
|
const keypair = await ws.cryptoApi.createEddsaKeypair();
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
const canonExchange = canonicalizeBaseUrl(req.exchange);
|
const canonExchange = canonicalizeBaseUrl(req.exchange);
|
||||||
|
|
||||||
let reserveStatus;
|
let reserveStatus;
|
||||||
@ -217,7 +215,6 @@ export async function createReserve(
|
|||||||
timestampReserveInfoPosted: undefined,
|
timestampReserveInfoPosted: undefined,
|
||||||
bankInfo,
|
bankInfo,
|
||||||
reserveStatus,
|
reserveStatus,
|
||||||
lastSuccessfulStatusQuery: undefined,
|
|
||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
currency: req.amount.currency,
|
currency: req.amount.currency,
|
||||||
@ -403,7 +400,9 @@ async function registerReserveWithBank(
|
|||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
r.timestampReserveInfoPosted = getTimestampNow();
|
r.timestampReserveInfoPosted = AbsoluteTime.toTimestamp(
|
||||||
|
AbsoluteTime.now(),
|
||||||
|
);
|
||||||
r.reserveStatus = ReserveRecordStatus.WaitConfirmBank;
|
r.reserveStatus = ReserveRecordStatus.WaitConfirmBank;
|
||||||
r.operationStatus = OperationStatus.Pending;
|
r.operationStatus = OperationStatus.Pending;
|
||||||
if (!r.bankInfo) {
|
if (!r.bankInfo) {
|
||||||
@ -472,7 +471,7 @@ async function processReserveBankStatus(
|
|||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
r.timestampBankConfirmed = now;
|
r.timestampBankConfirmed = now;
|
||||||
r.reserveStatus = ReserveRecordStatus.BankAborted;
|
r.reserveStatus = ReserveRecordStatus.BankAborted;
|
||||||
r.operationStatus = OperationStatus.Finished;
|
r.operationStatus = OperationStatus.Finished;
|
||||||
@ -509,7 +508,7 @@ async function processReserveBankStatus(
|
|||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||||
r.timestampBankConfirmed = now;
|
r.timestampBankConfirmed = now;
|
||||||
r.reserveStatus = ReserveRecordStatus.QueryingStatus;
|
r.reserveStatus = ReserveRecordStatus.QueryingStatus;
|
||||||
r.operationStatus = OperationStatus.Pending;
|
r.operationStatus = OperationStatus.Pending;
|
||||||
@ -683,7 +682,7 @@ async function updateReserve(
|
|||||||
exchangeBaseUrl: reserve.exchangeBaseUrl,
|
exchangeBaseUrl: reserve.exchangeBaseUrl,
|
||||||
reservePub: reserve.reservePub,
|
reservePub: reserve.reservePub,
|
||||||
rawWithdrawalAmount: remainingAmount,
|
rawWithdrawalAmount: remainingAmount,
|
||||||
timestampStart: getTimestampNow(),
|
timestampStart: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
|
||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
denomsSel: denomSelectionInfoToState(denomSelInfo),
|
denomsSel: denomSelectionInfoToState(denomSelInfo),
|
||||||
@ -736,7 +735,7 @@ async function processReserveImpl(
|
|||||||
await resetReserveRetry(ws, reservePub);
|
await resetReserveRetry(ws, reservePub);
|
||||||
} else if (
|
} else if (
|
||||||
reserve.retryInfo &&
|
reserve.retryInfo &&
|
||||||
!Timestamp.isExpired(reserve.retryInfo.nextRetry)
|
!AbsoluteTime.isExpired(reserve.retryInfo.nextRetry)
|
||||||
) {
|
) {
|
||||||
logger.trace("processReserve retry not due yet");
|
logger.trace("processReserve retry not due yet");
|
||||||
return;
|
return;
|
||||||
|
@ -229,8 +229,8 @@ async function createOrder(
|
|||||||
amount,
|
amount,
|
||||||
summary,
|
summary,
|
||||||
fulfillment_url: fulfillmentUrl,
|
fulfillment_url: fulfillmentUrl,
|
||||||
refund_deadline: { t_ms: t * 1000 },
|
refund_deadline: { t_s: t },
|
||||||
wire_transfer_deadline: { t_ms: t * 1000 },
|
wire_transfer_deadline: { t_s: t },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const resp = await http.postJson(reqUrl, orderReq, {
|
const resp = await http.postJson(reqUrl, orderReq, {
|
||||||
|
@ -22,7 +22,6 @@ import {
|
|||||||
parseTipUri,
|
parseTipUri,
|
||||||
codecForTipPickupGetResponse,
|
codecForTipPickupGetResponse,
|
||||||
Amounts,
|
Amounts,
|
||||||
getTimestampNow,
|
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
TipPlanchetDetail,
|
TipPlanchetDetail,
|
||||||
@ -32,6 +31,7 @@ import {
|
|||||||
DenomKeyType,
|
DenomKeyType,
|
||||||
BlindedDenominationSignature,
|
BlindedDenominationSignature,
|
||||||
codecForMerchantTipResponseV2,
|
codecForMerchantTipResponseV2,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js";
|
import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js";
|
||||||
import {
|
import {
|
||||||
@ -39,6 +39,7 @@ import {
|
|||||||
CoinRecord,
|
CoinRecord,
|
||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
CoinStatus,
|
CoinStatus,
|
||||||
|
TipRecord,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { j2s } from "@gnu-taler/taler-util";
|
import { j2s } from "@gnu-taler/taler-util";
|
||||||
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
||||||
@ -115,14 +116,14 @@ export async function prepareTip(
|
|||||||
const secretSeed = encodeCrock(getRandomBytes(64));
|
const secretSeed = encodeCrock(getRandomBytes(64));
|
||||||
const denomSelUid = encodeCrock(getRandomBytes(32));
|
const denomSelUid = encodeCrock(getRandomBytes(32));
|
||||||
|
|
||||||
const newTipRecord = {
|
const newTipRecord: TipRecord = {
|
||||||
walletTipId: walletTipId,
|
walletTipId: walletTipId,
|
||||||
acceptedTimestamp: undefined,
|
acceptedTimestamp: undefined,
|
||||||
tipAmountRaw: amount,
|
tipAmountRaw: amount,
|
||||||
tipExpiration: tipPickupStatus.expiration,
|
tipExpiration: tipPickupStatus.expiration,
|
||||||
exchangeBaseUrl: tipPickupStatus.exchange_url,
|
exchangeBaseUrl: tipPickupStatus.exchange_url,
|
||||||
merchantBaseUrl: res.merchantBaseUrl,
|
merchantBaseUrl: res.merchantBaseUrl,
|
||||||
createdTimestamp: getTimestampNow(),
|
createdTimestamp: TalerProtocolTimestamp.now(),
|
||||||
merchantTipId: res.merchantTipId,
|
merchantTipId: res.merchantTipId,
|
||||||
tipAmountEffective: Amounts.sub(
|
tipAmountEffective: Amounts.sub(
|
||||||
amount,
|
amount,
|
||||||
@ -397,7 +398,7 @@ async function processTipImpl(
|
|||||||
if (tr.pickedUpTimestamp) {
|
if (tr.pickedUpTimestamp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tr.pickedUpTimestamp = getTimestampNow();
|
tr.pickedUpTimestamp = TalerProtocolTimestamp.now();
|
||||||
tr.lastError = undefined;
|
tr.lastError = undefined;
|
||||||
tr.retryInfo = initRetryInfo();
|
tr.retryInfo = initRetryInfo();
|
||||||
await tx.tips.put(tr);
|
await tx.tips.put(tr);
|
||||||
@ -421,7 +422,7 @@ export async function acceptTip(
|
|||||||
logger.error("tip not found");
|
logger.error("tip not found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
tipRecord.acceptedTimestamp = getTimestampNow();
|
tipRecord.acceptedTimestamp = TalerProtocolTimestamp.now();
|
||||||
await tx.tips.put(tipRecord);
|
await tx.tips.put(tipRecord);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -18,12 +18,12 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
AmountJson,
|
AmountJson,
|
||||||
Amounts,
|
Amounts,
|
||||||
Logger,
|
Logger,
|
||||||
OrderShortInfo,
|
OrderShortInfo,
|
||||||
PaymentStatus,
|
PaymentStatus,
|
||||||
timestampCmp,
|
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionsRequest,
|
TransactionsRequest,
|
||||||
TransactionsResponse,
|
TransactionsResponse,
|
||||||
@ -309,7 +309,7 @@ export async function getTransactions(
|
|||||||
|
|
||||||
for (const rk of Object.keys(pr.refunds)) {
|
for (const rk of Object.keys(pr.refunds)) {
|
||||||
const refund = pr.refunds[rk];
|
const refund = pr.refunds[rk];
|
||||||
const groupKey = `${refund.executionTime.t_ms}`;
|
const groupKey = `${refund.executionTime.t_s}`;
|
||||||
refundGroupKeys.add(groupKey);
|
refundGroupKeys.add(groupKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +333,7 @@ export async function getTransactions(
|
|||||||
let amountEffective = Amounts.getZero(contractData.amount.currency);
|
let amountEffective = Amounts.getZero(contractData.amount.currency);
|
||||||
for (const rk of Object.keys(pr.refunds)) {
|
for (const rk of Object.keys(pr.refunds)) {
|
||||||
const refund = pr.refunds[rk];
|
const refund = pr.refunds[rk];
|
||||||
const myGroupKey = `${refund.executionTime.t_ms}`;
|
const myGroupKey = `${refund.executionTime.t_s}`;
|
||||||
if (myGroupKey !== groupKey) {
|
if (myGroupKey !== groupKey) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -403,8 +403,18 @@ export async function getTransactions(
|
|||||||
const txPending = transactions.filter((x) => x.pending);
|
const txPending = transactions.filter((x) => x.pending);
|
||||||
const txNotPending = transactions.filter((x) => !x.pending);
|
const txNotPending = transactions.filter((x) => !x.pending);
|
||||||
|
|
||||||
txPending.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp));
|
txPending.sort((h1, h2) =>
|
||||||
txNotPending.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp));
|
AbsoluteTime.cmp(
|
||||||
|
AbsoluteTime.fromTimestamp(h1.timestamp),
|
||||||
|
AbsoluteTime.fromTimestamp(h2.timestamp),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
txNotPending.sort((h1, h2) =>
|
||||||
|
AbsoluteTime.cmp(
|
||||||
|
AbsoluteTime.fromTimestamp(h1.timestamp),
|
||||||
|
AbsoluteTime.fromTimestamp(h2.timestamp),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return { transactions: [...txNotPending, ...txPending] };
|
return { transactions: [...txNotPending, ...txPending] };
|
||||||
}
|
}
|
||||||
@ -485,11 +495,10 @@ export async function deleteTransaction(
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const reserveRecord:
|
const reserveRecord: ReserveRecord | undefined =
|
||||||
| ReserveRecord
|
await tx.reserves.indexes.byInitialWithdrawalGroupId.get(
|
||||||
| undefined = await tx.reserves.indexes.byInitialWithdrawalGroupId.get(
|
withdrawalGroupId,
|
||||||
withdrawalGroupId,
|
);
|
||||||
);
|
|
||||||
if (reserveRecord && !reserveRecord.initialWithdrawalStarted) {
|
if (reserveRecord && !reserveRecord.initialWithdrawalStarted) {
|
||||||
const reservePub = reserveRecord.reservePub;
|
const reservePub = reserveRecord.reservePub;
|
||||||
await tx.reserves.delete(reservePub);
|
await tx.reserves.delete(reservePub);
|
||||||
|
@ -62,16 +62,16 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
masterSig:
|
masterSig:
|
||||||
"4F0P456CNNTTWK8BFJHGM3JTD6FVVNZY8EP077GYAHDJ5Y81S5RQ3SMS925NXMDVG9A88JAAP0E2GDZBC21PP5NHFFVWHAW3AVT8J3R",
|
"4F0P456CNNTTWK8BFJHGM3JTD6FVVNZY8EP077GYAHDJ5Y81S5RQ3SMS925NXMDVG9A88JAAP0E2GDZBC21PP5NHFFVWHAW3AVT8J3R",
|
||||||
stampExpireDeposit: {
|
stampExpireDeposit: {
|
||||||
t_ms: 1742909388000,
|
t_s: 1742909388,
|
||||||
},
|
},
|
||||||
stampExpireLegal: {
|
stampExpireLegal: {
|
||||||
t_ms: 1900589388000,
|
t_s: 1900589388,
|
||||||
},
|
},
|
||||||
stampExpireWithdraw: {
|
stampExpireWithdraw: {
|
||||||
t_ms: 1679837388000,
|
t_s: 1679837388,
|
||||||
},
|
},
|
||||||
stampStart: {
|
stampStart: {
|
||||||
t_ms: 1585229388000,
|
t_s: 1585229388,
|
||||||
},
|
},
|
||||||
verificationStatus: DenominationVerificationStatus.Unverified,
|
verificationStatus: DenominationVerificationStatus.Unverified,
|
||||||
value: {
|
value: {
|
||||||
@ -79,7 +79,7 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
fraction: 0,
|
fraction: 0,
|
||||||
value: 1000,
|
value: 1000,
|
||||||
},
|
},
|
||||||
listIssueDate: { t_ms: 0 },
|
listIssueDate: { t_s: 0 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
denomPub: {
|
denomPub: {
|
||||||
@ -117,16 +117,16 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
masterSig:
|
masterSig:
|
||||||
"P99AW82W46MZ0AKW7Z58VQPXFNTJQM9DVTYPBDF6KVYF38PPVDAZTV7JQ8TY7HGEC7JJJAY4E7AY7J3W1WV10DAZZQHHKTAVTSRAC20",
|
"P99AW82W46MZ0AKW7Z58VQPXFNTJQM9DVTYPBDF6KVYF38PPVDAZTV7JQ8TY7HGEC7JJJAY4E7AY7J3W1WV10DAZZQHHKTAVTSRAC20",
|
||||||
stampExpireDeposit: {
|
stampExpireDeposit: {
|
||||||
t_ms: 1742909388000,
|
t_s: 1742909388,
|
||||||
},
|
},
|
||||||
stampExpireLegal: {
|
stampExpireLegal: {
|
||||||
t_ms: 1900589388000,
|
t_s: 1900589388,
|
||||||
},
|
},
|
||||||
stampExpireWithdraw: {
|
stampExpireWithdraw: {
|
||||||
t_ms: 1679837388000,
|
t_s: 1679837388,
|
||||||
},
|
},
|
||||||
stampStart: {
|
stampStart: {
|
||||||
t_ms: 1585229388000,
|
t_s: 1585229388,
|
||||||
},
|
},
|
||||||
verificationStatus: DenominationVerificationStatus.Unverified,
|
verificationStatus: DenominationVerificationStatus.Unverified,
|
||||||
value: {
|
value: {
|
||||||
@ -134,7 +134,7 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
fraction: 0,
|
fraction: 0,
|
||||||
value: 10,
|
value: 10,
|
||||||
},
|
},
|
||||||
listIssueDate: { t_ms: 0 },
|
listIssueDate: { t_s: 0 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
denomPub: {
|
denomPub: {
|
||||||
@ -171,16 +171,16 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
masterSig:
|
masterSig:
|
||||||
"8S4VZGHE5WE0N5ZVCHYW9KZZR4YAKK15S46MV1HR1QB9AAMH3NWPW4DCR4NYGJK33Q8YNFY80SWNS6XKAP5DEVK933TM894FJ2VGE3G",
|
"8S4VZGHE5WE0N5ZVCHYW9KZZR4YAKK15S46MV1HR1QB9AAMH3NWPW4DCR4NYGJK33Q8YNFY80SWNS6XKAP5DEVK933TM894FJ2VGE3G",
|
||||||
stampExpireDeposit: {
|
stampExpireDeposit: {
|
||||||
t_ms: 1742909388000,
|
t_s: 1742909388,
|
||||||
},
|
},
|
||||||
stampExpireLegal: {
|
stampExpireLegal: {
|
||||||
t_ms: 1900589388000,
|
t_s: 1900589388,
|
||||||
},
|
},
|
||||||
stampExpireWithdraw: {
|
stampExpireWithdraw: {
|
||||||
t_ms: 1679837388000,
|
t_s: 1679837388,
|
||||||
},
|
},
|
||||||
stampStart: {
|
stampStart: {
|
||||||
t_ms: 1585229388000,
|
t_s: 1585229388,
|
||||||
},
|
},
|
||||||
verificationStatus: DenominationVerificationStatus.Unverified,
|
verificationStatus: DenominationVerificationStatus.Unverified,
|
||||||
value: {
|
value: {
|
||||||
@ -188,7 +188,7 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
fraction: 0,
|
fraction: 0,
|
||||||
value: 5,
|
value: 5,
|
||||||
},
|
},
|
||||||
listIssueDate: { t_ms: 0 },
|
listIssueDate: { t_s: 0 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
denomPub: {
|
denomPub: {
|
||||||
@ -226,16 +226,16 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
masterSig:
|
masterSig:
|
||||||
"E3AWGAG8VB42P3KXM8B04Z6M483SX59R3Y4T53C3NXCA2NPB6C7HVCMVX05DC6S58E9X40NGEBQNYXKYMYCF3ASY2C4WP1WCZ4ME610",
|
"E3AWGAG8VB42P3KXM8B04Z6M483SX59R3Y4T53C3NXCA2NPB6C7HVCMVX05DC6S58E9X40NGEBQNYXKYMYCF3ASY2C4WP1WCZ4ME610",
|
||||||
stampExpireDeposit: {
|
stampExpireDeposit: {
|
||||||
t_ms: 1742909388000,
|
t_s: 1742909388,
|
||||||
},
|
},
|
||||||
stampExpireLegal: {
|
stampExpireLegal: {
|
||||||
t_ms: 1900589388000,
|
t_s: 1900589388,
|
||||||
},
|
},
|
||||||
stampExpireWithdraw: {
|
stampExpireWithdraw: {
|
||||||
t_ms: 1679837388000,
|
t_s: 1679837388,
|
||||||
},
|
},
|
||||||
stampStart: {
|
stampStart: {
|
||||||
t_ms: 1585229388000,
|
t_s: 1585229388,
|
||||||
},
|
},
|
||||||
verificationStatus: DenominationVerificationStatus.Unverified,
|
verificationStatus: DenominationVerificationStatus.Unverified,
|
||||||
value: {
|
value: {
|
||||||
@ -243,7 +243,7 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
fraction: 0,
|
fraction: 0,
|
||||||
value: 1,
|
value: 1,
|
||||||
},
|
},
|
||||||
listIssueDate: { t_ms: 0 },
|
listIssueDate: { t_s: 0 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
denomPub: {
|
denomPub: {
|
||||||
@ -280,16 +280,16 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
masterSig:
|
masterSig:
|
||||||
"0ES1RKV002XB4YP21SN0QB7RSDHGYT0XAE65JYN8AVJAA6H7JZFN7JADXT521DJS89XMGPZGR8GCXF1516Y0Q9QDV00E6NMFA6CF838",
|
"0ES1RKV002XB4YP21SN0QB7RSDHGYT0XAE65JYN8AVJAA6H7JZFN7JADXT521DJS89XMGPZGR8GCXF1516Y0Q9QDV00E6NMFA6CF838",
|
||||||
stampExpireDeposit: {
|
stampExpireDeposit: {
|
||||||
t_ms: 1742909388000,
|
t_s: 1742909388,
|
||||||
},
|
},
|
||||||
stampExpireLegal: {
|
stampExpireLegal: {
|
||||||
t_ms: 1900589388000,
|
t_s: 1900589388,
|
||||||
},
|
},
|
||||||
stampExpireWithdraw: {
|
stampExpireWithdraw: {
|
||||||
t_ms: 1679837388000,
|
t_s: 1679837388,
|
||||||
},
|
},
|
||||||
stampStart: {
|
stampStart: {
|
||||||
t_ms: 1585229388000,
|
t_s: 1585229388,
|
||||||
},
|
},
|
||||||
verificationStatus: DenominationVerificationStatus.Unverified,
|
verificationStatus: DenominationVerificationStatus.Unverified,
|
||||||
value: {
|
value: {
|
||||||
@ -297,7 +297,7 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
fraction: 10000000,
|
fraction: 10000000,
|
||||||
value: 0,
|
value: 0,
|
||||||
},
|
},
|
||||||
listIssueDate: { t_ms: 0 },
|
listIssueDate: { t_s: 0 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
denomPub: {
|
denomPub: {
|
||||||
@ -334,16 +334,16 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
masterSig:
|
masterSig:
|
||||||
"58QEB6C6N7602E572E3JYANVVJ9BRW0V9E2ZFDW940N47YVQDK9SAFPWBN5YGT3G1742AFKQ0CYR4DM2VWV0Z0T1XMEKWN6X2EZ9M0R",
|
"58QEB6C6N7602E572E3JYANVVJ9BRW0V9E2ZFDW940N47YVQDK9SAFPWBN5YGT3G1742AFKQ0CYR4DM2VWV0Z0T1XMEKWN6X2EZ9M0R",
|
||||||
stampExpireDeposit: {
|
stampExpireDeposit: {
|
||||||
t_ms: 1742909388000,
|
t_s: 1742909388,
|
||||||
},
|
},
|
||||||
stampExpireLegal: {
|
stampExpireLegal: {
|
||||||
t_ms: 1900589388000,
|
t_s: 1900589388,
|
||||||
},
|
},
|
||||||
stampExpireWithdraw: {
|
stampExpireWithdraw: {
|
||||||
t_ms: 1679837388000,
|
t_s: 1679837388,
|
||||||
},
|
},
|
||||||
stampStart: {
|
stampStart: {
|
||||||
t_ms: 1585229388000,
|
t_s: 1585229388,
|
||||||
},
|
},
|
||||||
verificationStatus: DenominationVerificationStatus.Unverified,
|
verificationStatus: DenominationVerificationStatus.Unverified,
|
||||||
value: {
|
value: {
|
||||||
@ -351,7 +351,7 @@ test("withdrawal selection bug repro", (t) => {
|
|||||||
fraction: 0,
|
fraction: 0,
|
||||||
value: 2,
|
value: 2,
|
||||||
},
|
},
|
||||||
listIssueDate: { t_ms: 0 },
|
listIssueDate: { t_s: 0 },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -26,16 +26,12 @@ import {
|
|||||||
codecForWithdrawResponse,
|
codecForWithdrawResponse,
|
||||||
durationFromSpec,
|
durationFromSpec,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
getDurationRemaining,
|
|
||||||
getTimestampNow,
|
|
||||||
Logger,
|
Logger,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
parseWithdrawUri,
|
parseWithdrawUri,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
timestampCmp,
|
|
||||||
timestampSubtractDuraction,
|
|
||||||
WithdrawResponse,
|
WithdrawResponse,
|
||||||
URL,
|
URL,
|
||||||
WithdrawUriInfoResponse,
|
WithdrawUriInfoResponse,
|
||||||
@ -44,6 +40,8 @@ import {
|
|||||||
LibtoolVersion,
|
LibtoolVersion,
|
||||||
UnblindedSignature,
|
UnblindedSignature,
|
||||||
ExchangeWithdrawRequest,
|
ExchangeWithdrawRequest,
|
||||||
|
Duration,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
@ -147,7 +145,7 @@ export interface ExchangeWithdrawDetails {
|
|||||||
/**
|
/**
|
||||||
* The earliest deposit expiration of the selected coins.
|
* The earliest deposit expiration of the selected coins.
|
||||||
*/
|
*/
|
||||||
earliestDepositExpiration: Timestamp;
|
earliestDepositExpiration: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of currently offered denominations.
|
* Number of currently offered denominations.
|
||||||
@ -184,18 +182,20 @@ export interface ExchangeWithdrawDetails {
|
|||||||
* revocation and offered state.
|
* revocation and offered state.
|
||||||
*/
|
*/
|
||||||
export function isWithdrawableDenom(d: DenominationRecord): boolean {
|
export function isWithdrawableDenom(d: DenominationRecord): boolean {
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.now();
|
||||||
const started = timestampCmp(now, d.stampStart) >= 0;
|
const start = AbsoluteTime.fromTimestamp(d.stampStart);
|
||||||
let lastPossibleWithdraw: Timestamp;
|
const withdrawExpire = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
|
||||||
|
const started = AbsoluteTime.cmp(now, start) >= 0;
|
||||||
|
let lastPossibleWithdraw: AbsoluteTime;
|
||||||
if (walletCoreDebugFlags.denomselAllowLate) {
|
if (walletCoreDebugFlags.denomselAllowLate) {
|
||||||
lastPossibleWithdraw = d.stampExpireWithdraw;
|
lastPossibleWithdraw = start;
|
||||||
} else {
|
} else {
|
||||||
lastPossibleWithdraw = timestampSubtractDuraction(
|
lastPossibleWithdraw = AbsoluteTime.subtractDuraction(
|
||||||
d.stampExpireWithdraw,
|
withdrawExpire,
|
||||||
durationFromSpec({ minutes: 5 }),
|
durationFromSpec({ minutes: 5 }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const remaining = getDurationRemaining(lastPossibleWithdraw, now);
|
const remaining = Duration.getRemaining(lastPossibleWithdraw, now);
|
||||||
const stillOkay = remaining.d_ms !== 0;
|
const stillOkay = remaining.d_ms !== 0;
|
||||||
return started && stillOkay && !d.isRevoked && d.isOffered;
|
return started && stillOkay && !d.isRevoked && d.isOffered;
|
||||||
}
|
}
|
||||||
@ -274,7 +274,7 @@ export function selectWithdrawalDenominations(
|
|||||||
/**
|
/**
|
||||||
* Get information about a withdrawal from
|
* Get information about a withdrawal from
|
||||||
* a taler://withdraw URI by asking the bank.
|
* a taler://withdraw URI by asking the bank.
|
||||||
*
|
*
|
||||||
* FIXME: Move into bank client.
|
* FIXME: Move into bank client.
|
||||||
*/
|
*/
|
||||||
export async function getBankWithdrawalInfo(
|
export async function getBankWithdrawalInfo(
|
||||||
@ -947,7 +947,7 @@ async function processWithdrawGroupImpl(
|
|||||||
logger.trace(`now withdrawn ${numFinished} of ${numTotalCoins} coins`);
|
logger.trace(`now withdrawn ${numFinished} of ${numTotalCoins} coins`);
|
||||||
if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {
|
if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {
|
||||||
finishedForFirstTime = true;
|
finishedForFirstTime = true;
|
||||||
wg.timestampFinish = getTimestampNow();
|
wg.timestampFinish = TalerProtocolTimestamp.now();
|
||||||
wg.operationStatus = OperationStatus.Finished;
|
wg.operationStatus = OperationStatus.Finished;
|
||||||
delete wg.lastError;
|
delete wg.lastError;
|
||||||
wg.retryInfo = initRetryInfo();
|
wg.retryInfo = initRetryInfo();
|
||||||
@ -999,7 +999,12 @@ export async function getExchangeWithdrawalInfo(
|
|||||||
for (let i = 1; i < selectedDenoms.selectedDenoms.length; i++) {
|
for (let i = 1; i < selectedDenoms.selectedDenoms.length; i++) {
|
||||||
const expireDeposit =
|
const expireDeposit =
|
||||||
selectedDenoms.selectedDenoms[i].denom.stampExpireDeposit;
|
selectedDenoms.selectedDenoms[i].denom.stampExpireDeposit;
|
||||||
if (expireDeposit.t_ms < earliestDepositExpiration.t_ms) {
|
if (
|
||||||
|
AbsoluteTime.cmp(
|
||||||
|
AbsoluteTime.fromTimestamp(expireDeposit),
|
||||||
|
AbsoluteTime.fromTimestamp(earliestDepositExpiration),
|
||||||
|
) < 0
|
||||||
|
) {
|
||||||
earliestDepositExpiration = expireDeposit;
|
earliestDepositExpiration = expireDeposit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
import {
|
import {
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
BalancesResponse,
|
BalancesResponse,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { ReserveRecordStatus } from "./db.js";
|
import { ReserveRecordStatus } from "./db.js";
|
||||||
import { RetryInfo } from "./util/retries.js";
|
import { RetryInfo } from "./util/retries.js";
|
||||||
@ -112,7 +113,7 @@ export interface PendingReserveTask {
|
|||||||
type: PendingTaskType.Reserve;
|
type: PendingTaskType.Reserve;
|
||||||
retryInfo: RetryInfo | undefined;
|
retryInfo: RetryInfo | undefined;
|
||||||
stage: ReserveRecordStatus;
|
stage: ReserveRecordStatus;
|
||||||
timestampCreated: Timestamp;
|
timestampCreated: TalerProtocolTimestamp;
|
||||||
reserveType: ReserveType;
|
reserveType: ReserveType;
|
||||||
reservePub: string;
|
reservePub: string;
|
||||||
bankWithdrawConfirmUrl?: string;
|
bankWithdrawConfirmUrl?: string;
|
||||||
@ -135,7 +136,7 @@ export interface PendingRefreshTask {
|
|||||||
export interface PendingProposalDownloadTask {
|
export interface PendingProposalDownloadTask {
|
||||||
type: PendingTaskType.ProposalDownload;
|
type: PendingTaskType.ProposalDownload;
|
||||||
merchantBaseUrl: string;
|
merchantBaseUrl: string;
|
||||||
proposalTimestamp: Timestamp;
|
proposalTimestamp: TalerProtocolTimestamp;
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
orderId: string;
|
orderId: string;
|
||||||
lastError?: TalerErrorDetails;
|
lastError?: TalerErrorDetails;
|
||||||
@ -149,7 +150,7 @@ export interface PendingProposalDownloadTask {
|
|||||||
export interface PendingProposalChoiceOperation {
|
export interface PendingProposalChoiceOperation {
|
||||||
type: PendingTaskType.ProposalChoice;
|
type: PendingTaskType.ProposalChoice;
|
||||||
merchantBaseUrl: string;
|
merchantBaseUrl: string;
|
||||||
proposalTimestamp: Timestamp;
|
proposalTimestamp: AbsoluteTime;
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +232,7 @@ export interface PendingTaskInfoCommon {
|
|||||||
/**
|
/**
|
||||||
* Timestamp when the pending operation should be executed next.
|
* Timestamp when the pending operation should be executed next.
|
||||||
*/
|
*/
|
||||||
timestampDue: Timestamp;
|
timestampDue: AbsoluteTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retry info. Currently used to stop the wallet after any operation
|
* Retry info. Currently used to stop the wallet after any operation
|
||||||
|
@ -28,10 +28,7 @@ import { OperationFailedError, makeErrorDetails } from "../errors.js";
|
|||||||
import {
|
import {
|
||||||
Logger,
|
Logger,
|
||||||
Duration,
|
Duration,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
getTimestampNow,
|
|
||||||
timestampAddDuration,
|
|
||||||
timestampMax,
|
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Codec,
|
Codec,
|
||||||
j2s,
|
j2s,
|
||||||
@ -314,24 +311,24 @@ export async function readSuccessResponseTextOrThrow<T>(
|
|||||||
/**
|
/**
|
||||||
* Get the timestamp at which the response's content is considered expired.
|
* Get the timestamp at which the response's content is considered expired.
|
||||||
*/
|
*/
|
||||||
export function getExpiryTimestamp(
|
export function getExpiry(
|
||||||
httpResponse: HttpResponse,
|
httpResponse: HttpResponse,
|
||||||
opt: { minDuration?: Duration },
|
opt: { minDuration?: Duration },
|
||||||
): Timestamp {
|
): AbsoluteTime {
|
||||||
const expiryDateMs = new Date(
|
const expiryDateMs = new Date(
|
||||||
httpResponse.headers.get("expiry") ?? "",
|
httpResponse.headers.get("expiry") ?? "",
|
||||||
).getTime();
|
).getTime();
|
||||||
let t: Timestamp;
|
let t: AbsoluteTime;
|
||||||
if (Number.isNaN(expiryDateMs)) {
|
if (Number.isNaN(expiryDateMs)) {
|
||||||
t = getTimestampNow();
|
t = AbsoluteTime.now();
|
||||||
} else {
|
} else {
|
||||||
t = {
|
t = {
|
||||||
t_ms: expiryDateMs,
|
t_ms: expiryDateMs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (opt.minDuration) {
|
if (opt.minDuration) {
|
||||||
const t2 = timestampAddDuration(getTimestampNow(), opt.minDuration);
|
const t2 = AbsoluteTime.addDuration(AbsoluteTime.now(), opt.minDuration);
|
||||||
return timestampMax(t, t2);
|
return AbsoluteTime.max(t, t2);
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,11 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { Timestamp, Duration, getTimestampNow } from "@gnu-taler/taler-util";
|
import { AbsoluteTime, Duration } from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
export interface RetryInfo {
|
export interface RetryInfo {
|
||||||
firstTry: Timestamp;
|
firstTry: AbsoluteTime;
|
||||||
nextRetry: Timestamp;
|
nextRetry: AbsoluteTime;
|
||||||
retryCounter: number;
|
retryCounter: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ export function updateRetryInfoTimeout(
|
|||||||
r: RetryInfo,
|
r: RetryInfo,
|
||||||
p: RetryPolicy = defaultRetryPolicy,
|
p: RetryPolicy = defaultRetryPolicy,
|
||||||
): void {
|
): void {
|
||||||
const now = getTimestampNow();
|
const now = AbsoluteTime.now();
|
||||||
if (now.t_ms === "never") {
|
if (now.t_ms === "never") {
|
||||||
throw Error("assertion failed");
|
throw Error("assertion failed");
|
||||||
}
|
}
|
||||||
@ -54,10 +54,14 @@ export function updateRetryInfoTimeout(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextIncrement = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter)
|
const nextIncrement =
|
||||||
|
p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
|
||||||
|
|
||||||
const t =
|
const t =
|
||||||
now.t_ms + (p.maxTimeout.d_ms === "forever" ? nextIncrement : Math.min(p.maxTimeout.d_ms, nextIncrement));
|
now.t_ms +
|
||||||
|
(p.maxTimeout.d_ms === "forever"
|
||||||
|
? nextIncrement
|
||||||
|
: Math.min(p.maxTimeout.d_ms, nextIncrement));
|
||||||
r.nextRetry = { t_ms: t };
|
r.nextRetry = { t_ms: t };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,13 +77,13 @@ export function getRetryDuration(
|
|||||||
return { d_ms: "forever" };
|
return { d_ms: "forever" };
|
||||||
}
|
}
|
||||||
const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
|
const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
|
||||||
return { d_ms: p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t) };
|
return {
|
||||||
|
d_ms: p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initRetryInfo(
|
export function initRetryInfo(p: RetryPolicy = defaultRetryPolicy): RetryInfo {
|
||||||
p: RetryPolicy = defaultRetryPolicy,
|
const now = AbsoluteTime.now();
|
||||||
): RetryInfo {
|
|
||||||
const now = getTimestampNow();
|
|
||||||
const info = {
|
const info = {
|
||||||
firstTry: now,
|
firstTry: now,
|
||||||
nextRetry: now,
|
nextRetry: now,
|
||||||
|
@ -64,9 +64,7 @@ import {
|
|||||||
durationMin,
|
durationMin,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
ExchangesListRespose,
|
ExchangesListRespose,
|
||||||
getDurationRemaining,
|
|
||||||
GetExchangeTosResult,
|
GetExchangeTosResult,
|
||||||
isTimestampExpired,
|
|
||||||
j2s,
|
j2s,
|
||||||
KnownBankAccounts,
|
KnownBankAccounts,
|
||||||
Logger,
|
Logger,
|
||||||
@ -76,11 +74,12 @@ import {
|
|||||||
PaytoUri,
|
PaytoUri,
|
||||||
RefreshReason,
|
RefreshReason,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
timestampMin,
|
|
||||||
URL,
|
URL,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
|
Duration,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
import { timeStamp } from "console";
|
||||||
import {
|
import {
|
||||||
DenomInfo,
|
DenomInfo,
|
||||||
ExchangeOperations,
|
ExchangeOperations,
|
||||||
@ -292,7 +291,7 @@ export async function runPending(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const pendingOpsResponse = await getPendingOperations(ws);
|
const pendingOpsResponse = await getPendingOperations(ws);
|
||||||
for (const p of pendingOpsResponse.pendingOperations) {
|
for (const p of pendingOpsResponse.pendingOperations) {
|
||||||
if (!forceNow && !isTimestampExpired(p.timestampDue)) {
|
if (!forceNow && !AbsoluteTime.isExpired(p.timestampDue)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -340,10 +339,10 @@ async function runTaskLoop(
|
|||||||
logger.trace(`pending operations: ${j2s(pending)}`);
|
logger.trace(`pending operations: ${j2s(pending)}`);
|
||||||
let numGivingLiveness = 0;
|
let numGivingLiveness = 0;
|
||||||
let numDue = 0;
|
let numDue = 0;
|
||||||
let minDue: Timestamp = { t_ms: "never" };
|
let minDue: AbsoluteTime = AbsoluteTime.never();
|
||||||
for (const p of pending.pendingOperations) {
|
for (const p of pending.pendingOperations) {
|
||||||
minDue = timestampMin(minDue, p.timestampDue);
|
minDue = AbsoluteTime.min(minDue, p.timestampDue);
|
||||||
if (isTimestampExpired(p.timestampDue)) {
|
if (AbsoluteTime.isExpired(p.timestampDue)) {
|
||||||
numDue++;
|
numDue++;
|
||||||
}
|
}
|
||||||
if (p.givesLifeness) {
|
if (p.givesLifeness) {
|
||||||
@ -377,7 +376,7 @@ async function runTaskLoop(
|
|||||||
durationFromSpec({
|
durationFromSpec({
|
||||||
seconds: 5,
|
seconds: 5,
|
||||||
}),
|
}),
|
||||||
getDurationRemaining(minDue),
|
Duration.getRemaining(minDue),
|
||||||
);
|
);
|
||||||
logger.trace(`waiting for at most ${dt.d_ms} ms`);
|
logger.trace(`waiting for at most ${dt.d_ms} ms`);
|
||||||
const timeout = ws.timerGroup.resolveAfter(dt);
|
const timeout = ws.timerGroup.resolveAfter(dt);
|
||||||
@ -394,7 +393,7 @@ async function runTaskLoop(
|
|||||||
`running ${pending.pendingOperations.length} pending operations`,
|
`running ${pending.pendingOperations.length} pending operations`,
|
||||||
);
|
);
|
||||||
for (const p of pending.pendingOperations) {
|
for (const p of pending.pendingOperations) {
|
||||||
if (!isTimestampExpired(p.timestampDue)) {
|
if (!AbsoluteTime.isExpired(p.timestampDue)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -22,7 +22,11 @@
|
|||||||
import { PendingTransactionsView as TestedComponent } from "./PendingTransactions";
|
import { PendingTransactionsView as TestedComponent } from "./PendingTransactions";
|
||||||
import { Fragment, h, VNode } from "preact";
|
import { Fragment, h, VNode } from "preact";
|
||||||
import { createExample } from "../test-utils";
|
import { createExample } from "../test-utils";
|
||||||
import { Transaction, TransactionType } from "@gnu-taler/taler-util";
|
import {
|
||||||
|
TalerProtocolTimestamp,
|
||||||
|
Transaction,
|
||||||
|
TransactionType,
|
||||||
|
} from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "component/PendingTransactions",
|
title: "component/PendingTransactions",
|
||||||
@ -34,9 +38,7 @@ export const OnePendingTransaction = createExample(TestedComponent, {
|
|||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -46,23 +48,17 @@ export const ThreePendingTransactions = createExample(TestedComponent, {
|
|||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -72,72 +68,52 @@ export const TenPendingTransactions = createExample(TestedComponent, {
|
|||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1)
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
{
|
{
|
||||||
amountEffective: "USD:10",
|
amountEffective: "USD:10",
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 1,
|
|
||||||
},
|
|
||||||
} as Transaction,
|
} as Transaction,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { Amounts, NotificationType, Transaction } from "@gnu-taler/taler-util";
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
|
Amounts,
|
||||||
|
NotificationType,
|
||||||
|
Transaction,
|
||||||
|
} from "@gnu-taler/taler-util";
|
||||||
import { PendingTaskInfo } from "@gnu-taler/taler-wallet-core";
|
import { PendingTaskInfo } from "@gnu-taler/taler-wallet-core";
|
||||||
import { Fragment, h, JSX } from "preact";
|
import { Fragment, h, JSX } from "preact";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
|
||||||
@ -70,7 +75,11 @@ export function PendingTransactionsView({
|
|||||||
<b>
|
<b>
|
||||||
{amount.currency} {Amounts.stringifyValue(amount)}
|
{amount.currency} {Amounts.stringifyValue(amount)}
|
||||||
</b>{" "}
|
</b>{" "}
|
||||||
- <Time timestamp={t.timestamp} format="dd MMMM yyyy" />
|
-{" "}
|
||||||
|
<Time
|
||||||
|
timestamp={AbsoluteTime.fromTimestamp(t.timestamp)}
|
||||||
|
format="dd MMMM yyyy"
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Timestamp } from "@gnu-taler/taler-util";
|
import { AbsoluteTime } from "@gnu-taler/taler-util";
|
||||||
import { formatISO, format } from "date-fns";
|
import { formatISO, format } from "date-fns";
|
||||||
import { h, VNode } from "preact";
|
import { h, VNode } from "preact";
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export function Time({
|
|||||||
timestamp,
|
timestamp,
|
||||||
format: formatString,
|
format: formatString,
|
||||||
}: {
|
}: {
|
||||||
timestamp: Timestamp | undefined;
|
timestamp: AbsoluteTime | undefined;
|
||||||
format: string;
|
format: string;
|
||||||
}): VNode {
|
}): VNode {
|
||||||
return (
|
return (
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
AmountJson,
|
AmountJson,
|
||||||
Amounts,
|
Amounts,
|
||||||
AmountString,
|
AmountString,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionType,
|
TransactionType,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
@ -46,7 +46,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
|||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"credit"}
|
debitCreditIndicator={"credit"}
|
||||||
title={new URL(tx.exchangeBaseUrl).hostname}
|
title={new URL(tx.exchangeBaseUrl).hostname}
|
||||||
timestamp={tx.timestamp}
|
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||||
iconPath={"W"}
|
iconPath={"W"}
|
||||||
pending={tx.pending}
|
pending={tx.pending}
|
||||||
/>
|
/>
|
||||||
@ -59,7 +59,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
|||||||
debitCreditIndicator={"debit"}
|
debitCreditIndicator={"debit"}
|
||||||
title={tx.info.merchant.name}
|
title={tx.info.merchant.name}
|
||||||
subtitle={tx.info.summary}
|
subtitle={tx.info.summary}
|
||||||
timestamp={tx.timestamp}
|
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||||
iconPath={"P"}
|
iconPath={"P"}
|
||||||
pending={tx.pending}
|
pending={tx.pending}
|
||||||
/>
|
/>
|
||||||
@ -72,7 +72,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
|||||||
debitCreditIndicator={"credit"}
|
debitCreditIndicator={"credit"}
|
||||||
subtitle={tx.info.summary}
|
subtitle={tx.info.summary}
|
||||||
title={tx.info.merchant.name}
|
title={tx.info.merchant.name}
|
||||||
timestamp={tx.timestamp}
|
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||||
iconPath={"R"}
|
iconPath={"R"}
|
||||||
pending={tx.pending}
|
pending={tx.pending}
|
||||||
/>
|
/>
|
||||||
@ -84,7 +84,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
|||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"credit"}
|
debitCreditIndicator={"credit"}
|
||||||
title={new URL(tx.merchantBaseUrl).hostname}
|
title={new URL(tx.merchantBaseUrl).hostname}
|
||||||
timestamp={tx.timestamp}
|
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||||
iconPath={"T"}
|
iconPath={"T"}
|
||||||
pending={tx.pending}
|
pending={tx.pending}
|
||||||
/>
|
/>
|
||||||
@ -96,7 +96,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
|||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"credit"}
|
debitCreditIndicator={"credit"}
|
||||||
title={new URL(tx.exchangeBaseUrl).hostname}
|
title={new URL(tx.exchangeBaseUrl).hostname}
|
||||||
timestamp={tx.timestamp}
|
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||||
iconPath={"R"}
|
iconPath={"R"}
|
||||||
pending={tx.pending}
|
pending={tx.pending}
|
||||||
/>
|
/>
|
||||||
@ -108,7 +108,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
|||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"debit"}
|
debitCreditIndicator={"debit"}
|
||||||
title={tx.targetPaytoUri}
|
title={tx.targetPaytoUri}
|
||||||
timestamp={tx.timestamp}
|
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||||
iconPath={"D"}
|
iconPath={"D"}
|
||||||
pending={tx.pending}
|
pending={tx.pending}
|
||||||
/>
|
/>
|
||||||
@ -165,7 +165,7 @@ function TransactionLayout(props: TransactionLayoutProps): VNode {
|
|||||||
interface TransactionLayoutProps {
|
interface TransactionLayoutProps {
|
||||||
debitCreditIndicator: "debit" | "credit" | "unknown";
|
debitCreditIndicator: "debit" | "credit" | "unknown";
|
||||||
amount: AmountString | "unknown";
|
amount: AmountString | "unknown";
|
||||||
timestamp: Timestamp;
|
timestamp: AbsoluteTime;
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { AbsoluteTime, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||||
import { createExample } from "../test-utils";
|
import { createExample } from "../test-utils";
|
||||||
import { View as TestedComponent } from "./Tip";
|
import { View as TestedComponent } from "./Tip";
|
||||||
|
|
||||||
@ -33,9 +34,7 @@ export const Accepted = createExample(TestedComponent, {
|
|||||||
accepted: true,
|
accepted: true,
|
||||||
merchantBaseUrl: "",
|
merchantBaseUrl: "",
|
||||||
exchangeBaseUrl: "",
|
exchangeBaseUrl: "",
|
||||||
expirationTimestamp: {
|
expirationTimestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 0,
|
|
||||||
},
|
|
||||||
tipAmountEffective: "USD:10",
|
tipAmountEffective: "USD:10",
|
||||||
tipAmountRaw: "USD:5",
|
tipAmountRaw: "USD:5",
|
||||||
walletTipId: "id",
|
walletTipId: "id",
|
||||||
@ -47,9 +46,7 @@ export const NotYetAccepted = createExample(TestedComponent, {
|
|||||||
accepted: false,
|
accepted: false,
|
||||||
merchantBaseUrl: "http://merchant.url/",
|
merchantBaseUrl: "http://merchant.url/",
|
||||||
exchangeBaseUrl: "http://exchange.url/",
|
exchangeBaseUrl: "http://exchange.url/",
|
||||||
expirationTimestamp: {
|
expirationTimestamp: TalerProtocolTimestamp.fromSeconds(1),
|
||||||
t_ms: 0,
|
|
||||||
},
|
|
||||||
tipAmountEffective: "USD:10",
|
tipAmountEffective: "USD:10",
|
||||||
tipAmountRaw: "USD:5",
|
tipAmountRaw: "USD:5",
|
||||||
walletTipId: "id",
|
walletTipId: "id",
|
||||||
|
@ -20,7 +20,7 @@ import {
|
|||||||
CoinDumpJson,
|
CoinDumpJson,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
Timestamp,
|
AbsoluteTime,
|
||||||
Translate,
|
Translate,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { PendingTaskInfo } from "@gnu-taler/taler-wallet-core";
|
import { PendingTaskInfo } from "@gnu-taler/taler-wallet-core";
|
||||||
|
@ -23,6 +23,7 @@ import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core";
|
|||||||
import { addDays } from "date-fns";
|
import { addDays } from "date-fns";
|
||||||
import { BackupView as TestedComponent } from "./BackupPage";
|
import { BackupView as TestedComponent } from "./BackupPage";
|
||||||
import { createExample } from "../test-utils";
|
import { createExample } from "../test-utils";
|
||||||
|
import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "wallet/backup/list",
|
title: "wallet/backup/list",
|
||||||
@ -40,9 +41,8 @@ export const LotOfProviders = createExample(TestedComponent, {
|
|||||||
active: true,
|
active: true,
|
||||||
name: "sync.demo",
|
name: "sync.demo",
|
||||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||||
lastSuccessfulBackupTimestamp: {
|
lastSuccessfulBackupTimestamp:
|
||||||
t_ms: 1625063925078,
|
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||||
},
|
|
||||||
paymentProposalIds: [
|
paymentProposalIds: [
|
||||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||||
],
|
],
|
||||||
@ -62,9 +62,8 @@ export const LotOfProviders = createExample(TestedComponent, {
|
|||||||
active: true,
|
active: true,
|
||||||
name: "sync.demo",
|
name: "sync.demo",
|
||||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||||
lastSuccessfulBackupTimestamp: {
|
lastSuccessfulBackupTimestamp:
|
||||||
t_ms: 1625063925078,
|
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||||
},
|
|
||||||
paymentProposalIds: [
|
paymentProposalIds: [
|
||||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||||
],
|
],
|
||||||
@ -172,9 +171,8 @@ export const OneProvider = createExample(TestedComponent, {
|
|||||||
active: true,
|
active: true,
|
||||||
name: "sync.demo",
|
name: "sync.demo",
|
||||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||||
lastSuccessfulBackupTimestamp: {
|
lastSuccessfulBackupTimestamp:
|
||||||
t_ms: 1625063925078,
|
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||||
},
|
|
||||||
paymentProposalIds: [
|
paymentProposalIds: [
|
||||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||||
],
|
],
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Timestamp, Translate } from "@gnu-taler/taler-util";
|
import { AbsoluteTime, Translate } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
ProviderInfo,
|
ProviderInfo,
|
||||||
ProviderPaymentPaid,
|
ProviderPaymentPaid,
|
||||||
@ -104,7 +104,13 @@ export function BackupView({
|
|||||||
<BackupLayout
|
<BackupLayout
|
||||||
key={idx}
|
key={idx}
|
||||||
status={provider.paymentStatus}
|
status={provider.paymentStatus}
|
||||||
timestamp={provider.lastSuccessfulBackupTimestamp}
|
timestamp={
|
||||||
|
provider.lastSuccessfulBackupTimestamp
|
||||||
|
? AbsoluteTime.fromTimestamp(
|
||||||
|
provider.lastSuccessfulBackupTimestamp,
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
id={provider.syncProviderBaseUrl}
|
id={provider.syncProviderBaseUrl}
|
||||||
active={provider.active}
|
active={provider.active}
|
||||||
title={provider.name}
|
title={provider.name}
|
||||||
@ -144,7 +150,7 @@ export function BackupView({
|
|||||||
|
|
||||||
interface TransactionLayoutProps {
|
interface TransactionLayoutProps {
|
||||||
status: ProviderPaymentStatus;
|
status: ProviderPaymentStatus;
|
||||||
timestamp?: Timestamp;
|
timestamp?: AbsoluteTime;
|
||||||
title: string;
|
title: string;
|
||||||
id: string;
|
id: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
@ -192,7 +198,7 @@ function BackupLayout(props: TransactionLayoutProps): VNode {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ExpirationText({ until }: { until: Timestamp }): VNode {
|
function ExpirationText({ until }: { until: AbsoluteTime }): VNode {
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -207,13 +213,13 @@ function ExpirationText({ until }: { until: Timestamp }): VNode {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function colorByTimeToExpire(d: Timestamp): string {
|
function colorByTimeToExpire(d: AbsoluteTime): string {
|
||||||
if (d.t_ms === "never") return "rgb(28, 184, 65)";
|
if (d.t_ms === "never") return "rgb(28, 184, 65)";
|
||||||
const months = differenceInMonths(d.t_ms, new Date());
|
const months = differenceInMonths(d.t_ms, new Date());
|
||||||
return months > 1 ? "rgb(28, 184, 65)" : "rgb(223, 117, 20)";
|
return months > 1 ? "rgb(28, 184, 65)" : "rgb(223, 117, 20)";
|
||||||
}
|
}
|
||||||
|
|
||||||
function daysUntil(d: Timestamp): string {
|
function daysUntil(d: AbsoluteTime): string {
|
||||||
if (d.t_ms === "never") return "";
|
if (d.t_ms === "never") return "";
|
||||||
const duration = intervalToDuration({
|
const duration = intervalToDuration({
|
||||||
start: d.t_ms,
|
start: d.t_ms,
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
PaymentStatus,
|
PaymentStatus,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
TransactionCommon,
|
TransactionCommon,
|
||||||
TransactionDeposit,
|
TransactionDeposit,
|
||||||
TransactionPayment,
|
TransactionPayment,
|
||||||
@ -45,9 +46,9 @@ const commonTransaction = () =>
|
|||||||
amountRaw: "USD:10",
|
amountRaw: "USD:10",
|
||||||
amountEffective: "USD:9",
|
amountEffective: "USD:9",
|
||||||
pending: false,
|
pending: false,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(
|
||||||
t_ms: new Date().getTime() - count++ * 1000 * 60 * 60 * 7,
|
new Date().getTime() - count++ * 60 * 60 * 7,
|
||||||
},
|
),
|
||||||
transactionId: "12",
|
transactionId: "12",
|
||||||
} as TransactionCommon);
|
} as TransactionCommon);
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ export function HistoryView({
|
|||||||
.filter((t) => t.amountRaw.split(":")[0] === selectedCurrency)
|
.filter((t) => t.amountRaw.split(":")[0] === selectedCurrency)
|
||||||
.reduce((rv, x) => {
|
.reduce((rv, x) => {
|
||||||
const theDate =
|
const theDate =
|
||||||
x.timestamp.t_ms === "never" ? 0 : normalizeToDay(x.timestamp.t_ms);
|
x.timestamp.t_s === "never" ? 0 : normalizeToDay(x.timestamp.t_s * 1000);
|
||||||
if (theDate) {
|
if (theDate) {
|
||||||
(rv[theDate] = rv[theDate] || []).push(x);
|
(rv[theDate] = rv[theDate] || []).push(x);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||||
import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core";
|
import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core";
|
||||||
import { createExample } from "../test-utils";
|
import { createExample } from "../test-utils";
|
||||||
import { ProviderView as TestedComponent } from "./ProviderDetailPage";
|
import { ProviderView as TestedComponent } from "./ProviderDetailPage";
|
||||||
@ -38,9 +39,8 @@ export const Active = createExample(TestedComponent, {
|
|||||||
active: true,
|
active: true,
|
||||||
name: "sync.demo",
|
name: "sync.demo",
|
||||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||||
lastSuccessfulBackupTimestamp: {
|
lastSuccessfulBackupTimestamp:
|
||||||
t_ms: 1625063925078,
|
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||||
},
|
|
||||||
paymentProposalIds: [
|
paymentProposalIds: [
|
||||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||||
],
|
],
|
||||||
@ -63,12 +63,10 @@ export const ActiveErrorSync = createExample(TestedComponent, {
|
|||||||
active: true,
|
active: true,
|
||||||
name: "sync.demo",
|
name: "sync.demo",
|
||||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||||
lastSuccessfulBackupTimestamp: {
|
lastSuccessfulBackupTimestamp:
|
||||||
t_ms: 1625063925078,
|
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||||
},
|
lastAttemptedBackupTimestamp:
|
||||||
lastAttemptedBackupTimestamp: {
|
TalerProtocolTimestamp.fromSeconds(1625063925078),
|
||||||
t_ms: 1625063925078,
|
|
||||||
},
|
|
||||||
paymentProposalIds: [
|
paymentProposalIds: [
|
||||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||||
],
|
],
|
||||||
@ -97,9 +95,8 @@ export const ActiveBackupProblemUnreadable = createExample(TestedComponent, {
|
|||||||
active: true,
|
active: true,
|
||||||
name: "sync.demo",
|
name: "sync.demo",
|
||||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||||
lastSuccessfulBackupTimestamp: {
|
lastSuccessfulBackupTimestamp:
|
||||||
t_ms: 1625063925078,
|
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||||
},
|
|
||||||
paymentProposalIds: [
|
paymentProposalIds: [
|
||||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||||
],
|
],
|
||||||
@ -125,9 +122,8 @@ export const ActiveBackupProblemDevice = createExample(TestedComponent, {
|
|||||||
active: true,
|
active: true,
|
||||||
name: "sync.demo",
|
name: "sync.demo",
|
||||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||||
lastSuccessfulBackupTimestamp: {
|
lastSuccessfulBackupTimestamp:
|
||||||
t_ms: 1625063925078,
|
TalerProtocolTimestamp.fromSeconds(1625063925078),
|
||||||
},
|
|
||||||
paymentProposalIds: [
|
paymentProposalIds: [
|
||||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||||
],
|
],
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as utils from "@gnu-taler/taler-util";
|
import * as utils from "@gnu-taler/taler-util";
|
||||||
|
import { AbsoluteTime } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
ProviderInfo,
|
ProviderInfo,
|
||||||
ProviderPaymentStatus,
|
ProviderPaymentStatus,
|
||||||
@ -122,7 +123,9 @@ export function ProviderView({
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const lb = info.lastSuccessfulBackupTimestamp;
|
const lb = info.lastSuccessfulBackupTimestamp
|
||||||
|
? AbsoluteTime.fromTimestamp(info.lastSuccessfulBackupTimestamp)
|
||||||
|
: undefined;
|
||||||
const isPaid =
|
const isPaid =
|
||||||
info.paymentStatus.type === ProviderPaymentType.Paid ||
|
info.paymentStatus.type === ProviderPaymentType.Paid ||
|
||||||
info.paymentStatus.type === ProviderPaymentType.TermsChanged;
|
info.paymentStatus.type === ProviderPaymentType.TermsChanged;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
PaymentStatus,
|
PaymentStatus,
|
||||||
|
TalerProtocolTimestamp,
|
||||||
TransactionCommon,
|
TransactionCommon,
|
||||||
TransactionDeposit,
|
TransactionDeposit,
|
||||||
TransactionPayment,
|
TransactionPayment,
|
||||||
@ -53,9 +54,7 @@ const commonTransaction = {
|
|||||||
amountRaw: "KUDOS:11",
|
amountRaw: "KUDOS:11",
|
||||||
amountEffective: "KUDOS:9.2",
|
amountEffective: "KUDOS:9.2",
|
||||||
pending: false,
|
pending: false,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.now(),
|
||||||
t_ms: new Date().getTime(),
|
|
||||||
},
|
|
||||||
transactionId: "12",
|
transactionId: "12",
|
||||||
} as TransactionCommon;
|
} as TransactionCommon;
|
||||||
|
|
||||||
@ -144,18 +143,14 @@ export const Withdraw = createExample(TestedComponent, {
|
|||||||
export const WithdrawOneMinuteAgo = createExample(TestedComponent, {
|
export const WithdrawOneMinuteAgo = createExample(TestedComponent, {
|
||||||
transaction: {
|
transaction: {
|
||||||
...exampleData.withdraw,
|
...exampleData.withdraw,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(new Date().getTime() - 60),
|
||||||
t_ms: new Date().getTime() - 60 * 1000,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const WithdrawOneMinuteAgoAndPending = createExample(TestedComponent, {
|
export const WithdrawOneMinuteAgoAndPending = createExample(TestedComponent, {
|
||||||
transaction: {
|
transaction: {
|
||||||
...exampleData.withdraw,
|
...exampleData.withdraw,
|
||||||
timestamp: {
|
timestamp: TalerProtocolTimestamp.fromSeconds(new Date().getTime() - 60),
|
||||||
t_ms: new Date().getTime() - 60 * 1000,
|
|
||||||
},
|
|
||||||
pending: true,
|
pending: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
AmountLike,
|
AmountLike,
|
||||||
Amounts,
|
Amounts,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
@ -137,9 +138,9 @@ export function TransactionView({
|
|||||||
}): VNode {
|
}): VNode {
|
||||||
const showRetry =
|
const showRetry =
|
||||||
transaction.error !== undefined ||
|
transaction.error !== undefined ||
|
||||||
transaction.timestamp.t_ms === "never" ||
|
transaction.timestamp.t_s === "never" ||
|
||||||
(transaction.pending &&
|
(transaction.pending &&
|
||||||
differenceInSeconds(new Date(), transaction.timestamp.t_ms) > 10);
|
differenceInSeconds(new Date(), transaction.timestamp.t_s * 1000) > 10);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -218,7 +219,7 @@ export function TransactionView({
|
|||||||
<h2>
|
<h2>
|
||||||
<i18n.Translate>Withdrawal</i18n.Translate>
|
<i18n.Translate>Withdrawal</i18n.Translate>
|
||||||
</h2>
|
</h2>
|
||||||
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
|
<Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} format="dd MMMM yyyy, HH:mm" />
|
||||||
{transaction.pending ? (
|
{transaction.pending ? (
|
||||||
transaction.withdrawalDetails.type ===
|
transaction.withdrawalDetails.type ===
|
||||||
WithdrawalType.ManualTransfer ? (
|
WithdrawalType.ManualTransfer ? (
|
||||||
@ -342,7 +343,7 @@ export function TransactionView({
|
|||||||
<h2>
|
<h2>
|
||||||
<i18n.Translate>Payment</i18n.Translate>
|
<i18n.Translate>Payment</i18n.Translate>
|
||||||
</h2>
|
</h2>
|
||||||
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
|
<Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} format="dd MMMM yyyy, HH:mm" />
|
||||||
<br />
|
<br />
|
||||||
<Part
|
<Part
|
||||||
big
|
big
|
||||||
@ -425,7 +426,7 @@ export function TransactionView({
|
|||||||
<h2>
|
<h2>
|
||||||
<i18n.Translate>Deposit</i18n.Translate>
|
<i18n.Translate>Deposit</i18n.Translate>
|
||||||
</h2>
|
</h2>
|
||||||
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
|
<Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} format="dd MMMM yyyy, HH:mm" />
|
||||||
<br />
|
<br />
|
||||||
<Part
|
<Part
|
||||||
big
|
big
|
||||||
@ -459,7 +460,7 @@ export function TransactionView({
|
|||||||
<h2>
|
<h2>
|
||||||
<i18n.Translate>Refresh</i18n.Translate>
|
<i18n.Translate>Refresh</i18n.Translate>
|
||||||
</h2>
|
</h2>
|
||||||
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
|
<Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} format="dd MMMM yyyy, HH:mm" />
|
||||||
<br />
|
<br />
|
||||||
<Part
|
<Part
|
||||||
big
|
big
|
||||||
@ -493,7 +494,7 @@ export function TransactionView({
|
|||||||
<h2>
|
<h2>
|
||||||
<i18n.Translate>Tip</i18n.Translate>
|
<i18n.Translate>Tip</i18n.Translate>
|
||||||
</h2>
|
</h2>
|
||||||
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
|
<Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} format="dd MMMM yyyy, HH:mm" />
|
||||||
<br />
|
<br />
|
||||||
<Part
|
<Part
|
||||||
big
|
big
|
||||||
@ -527,7 +528,10 @@ export function TransactionView({
|
|||||||
<h2>
|
<h2>
|
||||||
<i18n.Translate>Refund</i18n.Translate>
|
<i18n.Translate>Refund</i18n.Translate>
|
||||||
</h2>
|
</h2>
|
||||||
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
|
<Time
|
||||||
|
timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)}
|
||||||
|
format="dd MMMM yyyy, HH:mm"
|
||||||
|
/>
|
||||||
<br />
|
<br />
|
||||||
<Part
|
<Part
|
||||||
big
|
big
|
||||||
|
Loading…
Reference in New Issue
Block a user