gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-kotlin] branch master updated: Add refresh crypto with tes


From: gnunet
Subject: [taler-wallet-kotlin] branch master updated: Add refresh crypto with tests
Date: Thu, 02 Jul 2020 18:42:07 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a commit to branch master
in repository wallet-kotlin.

The following commit(s) were added to refs/heads/master by this push:
     new 4a712e1  Add refresh crypto with tests
4a712e1 is described below

commit 4a712e1da5bb66d7a7e27271e65e09d4910e2339
Author: Torsten Grote <t@grobox.de>
AuthorDate: Thu Jul 2 13:41:34 2020 -0300

    Add refresh crypto with tests
---
 build.gradle                                       |   1 +
 .../net/taler/wallet/kotlin/crypto/RsaBlinding.kt  |   6 +-
 .../net/taler/wallet/kotlin/crypto/RefreshTest.kt  | 481 +++++++++++++++++++++
 .../kotlin/net/taler/wallet/kotlin/Timestamp.kt    |   2 +
 .../net/taler/wallet/kotlin/crypto/Refresh.kt      | 256 +++++++++++
 5 files changed, 743 insertions(+), 3 deletions(-)

diff --git a/build.gradle b/build.gradle
index 8ae318e..2dce365 100644
--- a/build.gradle
+++ b/build.gradle
@@ -39,6 +39,7 @@ kotlin {
         commonMain {
             dependencies {
                 implementation kotlin('stdlib-common')
+                implementation "com.soywiz.korlibs.klock:klock:1.11.12"
             }
         }
         commonTest {
diff --git 
a/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/RsaBlinding.kt 
b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/RsaBlinding.kt
index 6158c52..715bf83 100644
--- a/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/RsaBlinding.kt
+++ b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/RsaBlinding.kt
@@ -1,10 +1,10 @@
 package net.taler.wallet.kotlin.crypto
 
 import java.math.BigInteger
+import kotlin.math.abs
 import kotlin.math.ceil
 import kotlin.math.floor
 
-@OptIn(ExperimentalStdlibApi::class)
 internal object RsaBlinding {
 
     fun rsaBlind(hm: ByteArray, bks: ByteArray, rsaPubEnc: ByteArray): 
ByteArray {
@@ -40,8 +40,8 @@ internal object RsaBlinding {
     }
 
     private fun rsaPubDecode(publicKey: ByteArray): RsaPublicKey {
-        val modulusLength = (publicKey[0].toInt() shl 8) or 
publicKey[1].toInt()
-        val exponentLength = (publicKey[2].toInt() shl 8) or 
publicKey[3].toInt()
+        val modulusLength = abs((publicKey[0].toInt() shl 8) or 
publicKey[1].toInt())
+        val exponentLength = abs((publicKey[2].toInt() shl 8) or 
publicKey[3].toInt())
         if (4 + exponentLength + modulusLength != publicKey.size) {
             throw Error("invalid RSA public key (format wrong)")
         }
diff --git 
a/src/androidTest/kotlin/net/taler/wallet/kotlin/crypto/RefreshTest.kt 
b/src/androidTest/kotlin/net/taler/wallet/kotlin/crypto/RefreshTest.kt
new file mode 100644
index 0000000..37cf10f
--- /dev/null
+++ b/src/androidTest/kotlin/net/taler/wallet/kotlin/crypto/RefreshTest.kt
@@ -0,0 +1,481 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under 
the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+ * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.kotlin.crypto
+
+import net.taler.wallet.kotlin.Amount
+import net.taler.wallet.kotlin.Base32Crockford
+import net.taler.wallet.kotlin.CoinRecord
+import net.taler.wallet.kotlin.CoinSourceType.WITHDRAW
+import net.taler.wallet.kotlin.CoinStatus.DORMANT
+import net.taler.wallet.kotlin.DenominationRecord
+import net.taler.wallet.kotlin.DenominationStatus
+import net.taler.wallet.kotlin.Timestamp
+import net.taler.wallet.kotlin.crypto.Refresh.DenominationSelectionInfo
+import net.taler.wallet.kotlin.crypto.Refresh.RefreshPlanchetRecord
+import net.taler.wallet.kotlin.crypto.Refresh.RefreshSessionRecord
+import net.taler.wallet.kotlin.crypto.Refresh.SelectedDenomination
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+// TODO move to commonTest once RsaBlinding is implemented everywhere
+class RefreshTest {
+
+    private val crypto = CryptoFactory.getCrypto()
+    private val refresh = Refresh(crypto)
+
+    private class RefreshVector(
+        val kappa: Int,
+        val meltCoin: CoinRecord,
+        val newCoinDenominations: DenominationSelectionInfo,
+        val meltFee: Amount,
+        val refreshSessionRecord: RefreshSessionRecord,
+        val kappaKeys: List<EcdheKeyPair>
+    )
+
+    @Test
+    fun testCreateRefreshSession() {
+        val vectors = listOf(
+            RefreshVector(
+                kappa = 3,
+                meltCoin = CoinRecord(
+                    blindingKey = 
"N2A8K8XVTMPKNRCEJ1B8GMWJM6TXWRGAN5HWPFGPXS3WY76EKB60",
+                    coinPriv = 
"884XH9DBQT9MA34YKCR7WPEP2HH3J1R9C3G7MSB8C3NHB0XEVZW0",
+                    coinPub = 
"3GSHK9JCTMEAZY7BMC1QQD28ABEJEJJJXHPVF2YXTET97WB81GNG",
+                    currentAmount = Amount("TESTKUDOS", 1, 96000000),
+                    denomPub = 
"020000YDCXEZRKA0RR18PBW1HGXAH9HM9PN5TPWSVB8TG9QP4KDNHDVS0BRM9ATZV5AW5238WSNJWXA7V6WHE5QGMKRTSZYZZ6DWNHMZGAKCBC3T1HWKVPBHTRJZK68CGRQQ7A6VBRPCRS26QP8Z89TFRVN9NAQC6ZR0CD6P269XNQXYQAARNVBB74QSHJJXPMB5HE6TQ1HHK6AZRNWG2001",
+                    denomPubHash = 
"9ZSE6E0271SRAZTHS08BYF8TGAY2Y1Q8Y7TYWM3ZM25THE3ZJYNKDCXR72YKVQSES1GXWGQVSF13YZ0Z2RQE5VRBMF79KJQP85R71X8",
+                    denomSig = 
"73XS5TEM2WNQWJJBBMYC77DCV98NKX949KAFNJX9KXW2H9QDE4FM7SEJ7RB6RHX99QJGVT6SD1N24GXQCJY1CZA57RKDP3DK2C9WJ8HZ09Y33MYV33TD7C325GQQJFSSZ1EC5JX06BYNGKB0VEHXAR7KHXVT0Y5MZXSD8X65GRNPFV8B3HKC6BBNGVVXXM28WA7VTA7EWJKM4",
+                    exchangeBaseUrl = "example.org", status = DORMANT, 
coinSource = WITHDRAW, suspended = false
+                ),
+                newCoinDenominations = DenominationSelectionInfo(
+                    totalCoinValue = Amount("TESTKUDOS", 1, 81000000),
+                    totalWithdrawCost = Amount("TESTKUDOS", 1, 92000000),
+                    selectedDenominations = listOf(
+                        SelectedDenomination(
+                            count = 1,
+                            denominationRecord = DenominationRecord(
+                                denomPub = 
"020000YDW0GZQGY9GSKTEESAD1ZS803D576X1HTVJM8CWEBPVGQ4GHMDD5SBQHJ462NPW9FJD437HYW69MJR5N4YABJWYZB6P7CZ7CZR0YD7KY1M7C291BQX4T18DGKCTDEDBMVH2CF4K4NWVA1FFX4AYDB5GRWJ0JBJRRAVQ1ZY8D9PX9TJT9HEVBXAMQQF2ESKG1JX5CXDX7AMY2TG2001",
+                                denomPubHash = 
"ZVZMM6GEFETH1S71BRKFKB03BTSTA8JZH26Q1PZED78NZ50EVVT0DMDBPDN5EBY90SK4AWT0J8CNPD7NB2TSCFMSKAS5V6DK26GEKT0",
+                                exchangeBaseUrl = "example.org",
+                                feeDeposit = Amount("TESTKUDOS", fraction = 
2000000, value = 0),
+                                feeRefresh = Amount("TESTKUDOS", fraction = 
3000000, value = 0),
+                                feeRefund = Amount("TESTKUDOS", fraction = 
1000000, value = 0),
+                                feeWithdraw = Amount("TESTKUDOS", fraction = 
2000000, value = 0),
+                                isOffered = true,
+                                isRevoked = false,
+                                masterSig = 
"1XDNXJXA9CY30Q5BTJW5YN8XSYCCFKKSVGR9TW5CFPD4YSTYY52YBHDEPEAX3AY1HWR0XHYQHRJCX156NZMAPQ4BPZ00EXTW0MA1018",
+                                stampExpireDeposit = Timestamp(1656768984000),
+                                stampExpireLegal = Timestamp(1688304984000),
+                                stampExpireWithdraw = Timestamp(1594301784000),
+                                stampStart = Timestamp(1593696984000),
+                                status = DenominationStatus.VerifiedGood,
+                                value = Amount("TESTKUDOS", fraction = 0, 
value = 1)
+                            )
+                        ),
+                        SelectedDenomination(
+                            count = 8,
+                            denominationRecord = DenominationRecord(
+                                denomPub = 
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                                denomPubHash = 
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                                exchangeBaseUrl = "example.org",
+                                feeDeposit = Amount("TESTKUDOS", fraction = 
1000000, value = 0),
+                                feeRefresh = Amount("TESTKUDOS", fraction = 
3000000, value = 0),
+                                feeRefund = Amount("TESTKUDOS", fraction = 
1000000, value = 0),
+                                feeWithdraw = Amount("TESTKUDOS", fraction = 
1000000, value = 0),
+                                isOffered = true,
+                                isRevoked = false,
+                                masterSig = 
"X4WYKHZVAQFRXKCAQ0QCA8VFMC32ESJP8M3BTTYW20SBR46Q1HRF9Y309SQVVH8TY3R8PJ81A4QFAABYEQR2D9RXTSVCBMD667Q723R",
+                                stampExpireDeposit = Timestamp(1656768984000),
+                                stampExpireLegal = Timestamp(1688304984000),
+                                stampExpireWithdraw = Timestamp(1594301784000),
+                                stampStart = Timestamp(1593696984000),
+                                status = DenominationStatus.VerifiedGood,
+                                value = Amount("TESTKUDOS", fraction = 
10000000, value = 0)
+                            )
+                        ),
+                        SelectedDenomination(
+                            count = 1,
+                            denominationRecord = DenominationRecord(
+                                denomPub = 
"020000Z46ESSPKMQ5875ZZMX9EZ4ZD7MQ3MPM4R2QJ7K7ADMCK0CJDM7W6GEZF0BQGWHVCMA4CY83NBYKBZYB9AYG293JD7VW7BSQTFH51HK02Q96JRTXBAY2DS8ZEXQFXRM6SD43C6BJCBDRCSXXEXA1WG2FFG5CX65TZW94274CMHJNVAY6VR8Y8XKV928P7AHBK106R6TCTS164CG2001",
+                                denomPubHash = 
"0F44ZC9M6XB828XW09DY2YZNM07DA0CZV72D69F31RXH1CQHF3GDPVGZT5135BJEHVX0RGN44SCWS8CR8E39TDSRW10GZ6AEB2R40E8",
+                                exchangeBaseUrl = "example.org",
+                                feeDeposit = Amount("TESTKUDOS", fraction = 
1000000, value = 0),
+                                feeRefresh = Amount("TESTKUDOS", fraction = 
1000000, value = 0),
+                                feeRefund = Amount("TESTKUDOS", fraction = 
1000000, value = 0),
+                                feeWithdraw = Amount("TESTKUDOS", fraction = 
1000000, value = 0),
+                                isOffered = true,
+                                isRevoked = false,
+                                masterSig = 
"8DGF50VWCB2JHM613NRJM0NQYAVWFXCSBAHKTG3ZHKQ7N59WXK7MA4V794A97YDG0AVP350B2NT1QQ66VS1H2QG3P29PM5QDE6ZH60R",
+                                stampExpireDeposit = Timestamp(1656768984000),
+                                stampExpireLegal = Timestamp(1688304984000),
+                                stampExpireWithdraw = Timestamp(1594301784000),
+                                stampStart = Timestamp(1593696984000),
+                                status = DenominationStatus.VerifiedGood,
+                                value = Amount("TESTKUDOS", fraction = 
1000000, value = 0)
+                            )
+                        )
+                    )
+                ),
+                meltFee = Amount("TESTKUDOS", fraction = 3000000, value = 0),
+                refreshSessionRecord = RefreshSessionRecord(
+                    confirmSig = 
"97N1DQ1FGXT6SFR4J8H9WXCNG741BE986E3YW59T6R4DZ1FPC3XRFQ9D6BNRHGTAANZQXH4T1KXH61DP8BGHY6SYHZJWVGSQBV0A00G",
+                    exchangeBaseUrl = "example.org",
+                    hash = 
"XCSSAYNJ6964PVG393PDEGX0CGGZBNY6YMRDNZYBCTBDJF8ASA0BEA5TK435GG1DCFTTK26SS3V0EQ0DB9XKC9M9PWHTE4SZK9JZPVG",
+                    meltCoinPub = 
"3GSHK9JCTMEAZY7BMC1QQD28ABEJEJJJXHPVF2YXTET97WB81GNG",
+                    newDenominationHashes = listOf(
+                        
"ZVZMM6GEFETH1S71BRKFKB03BTSTA8JZH26Q1PZED78NZ50EVVT0DMDBPDN5EBY90SK4AWT0J8CNPD7NB2TSCFMSKAS5V6DK26GEKT0",
+                        
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                        
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                        
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                        
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                        
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                        
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                        
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                        
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                        
"0F44ZC9M6XB828XW09DY2YZNM07DA0CZV72D69F31RXH1CQHF3GDPVGZT5135BJEHVX0RGN44SCWS8CR8E39TDSRW10GZ6AEB2R40E8"
+                    ),
+                    newDenominations = listOf(
+                        
"020000YDW0GZQGY9GSKTEESAD1ZS803D576X1HTVJM8CWEBPVGQ4GHMDD5SBQHJ462NPW9FJD437HYW69MJR5N4YABJWYZB6P7CZ7CZR0YD7KY1M7C291BQX4T18DGKCTDEDBMVH2CF4K4NWVA1FFX4AYDB5GRWJ0JBJRRAVQ1ZY8D9PX9TJT9HEVBXAMQQF2ESKG1JX5CXDX7AMY2TG2001",
+                        
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                        
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                        
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                        
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                        
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                        
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                        
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                        
"020000WTGW5YBES6D2CDGZWESS40NQMSS19R07QGQZTARCZ108VQXQKWQW97C6HM2937V05P4NJ7PNYJJS8DARNGDD2ENQ6ANEHK1PMJEJA1R2FBQKQNA463VX53HSW94RRJP631M9PGE7P32ZNM9DA0F1DJFHSSPZ6GPY3K7BRKW0PP3AZKMX475WP00B02XMHNDWGXR0J2NSWV81KG2001",
+                        
"020000Z46ESSPKMQ5875ZZMX9EZ4ZD7MQ3MPM4R2QJ7K7ADMCK0CJDM7W6GEZF0BQGWHVCMA4CY83NBYKBZYB9AYG293JD7VW7BSQTFH51HK02Q96JRTXBAY2DS8ZEXQFXRM6SD43C6BJCBDRCSXXEXA1WG2FFG5CX65TZW94274CMHJNVAY6VR8Y8XKV928P7AHBK106R6TCTS164CG2001"
+                    ),
+                    noRevealIndex = null,
+                    planchetsForGammas = listOf(
+                        listOf(
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"G17FNHNV8BA7BFZRG75PPKJSK7FC47GCXF8YKJZ491D115FQEGE0",
+                                coinEv =
+                                
"85BB17VN2XV7TTNKV5MS64JVA7X39TMCSQQQKWFT30SZFJDA4A9F06SGP89GF7ZZBAVB8X3VTT94SKRQ192ENJS4K12715NKHVH11XWVKND063AK16ANGV2P3XCP32Y4GRJGZMZ068EQS623C4CW75VB2KPF45TKEJ3VX4M4GXWMB3H07F8NQDQGMPFCBDS8SG2HXESC8DXVE",
+                                privateKey = 
"972E3RE0GFMMGXHZTYSFD0H3YDYGGBNH80F5GMHPCN7C8NQCH460",
+                                publicKey = 
"BH1Y1VBKNH86RT9FD191MP82W7PAQCA0SN9GPBN23N02M9NBYS50"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"QR15AD5SFG20S9KW70P1MFAGPQC9WK7MSD0VFQXHKPDZTVZ6BZV0",
+                                coinEv =
+                                
"DBKVPSHR1N8F04N1FQ04EPVBBWGETX5835M8Z9P7WF5X6BHBJW8KKNHZVR1JM9TRVZ9J0G5VDK7BQ9ESFKDCZD4PPB05K0MHW0HAJRKCCJ5PTNFXSRVB4V25D48XT031P0M53WDZRTPEK5G1XQ5KMQHZT06A70GE9N5C7SR4RDVA20AZDBBD1MKPR6NB2FMPAP2DR3NJENPWA",
+                                privateKey = 
"AZXBY4QCFSH759DWTDCK1Q5D5JRNH3663JP2RTC3TSSTH3ARV0D0",
+                                publicKey = 
"1C88APBTDQ8A2Q55N1CB3PD91PHYKJHY8QM2CYK0PGRJ0CTK4VPG"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"EP0XT6PDDJFKGFVG739QKNZCWRN05497TCBQERFRBJBB7W7Y2JV0",
+                                coinEv =
+                                
"J8E5HCMEJZ78BP2VDCNA9FVFBDG9QT4A1FDBSAH6ZZJ2HNJNKYKS57ZVRPZ47B1B8J6WV77PXCDP7EZTX5YTJFPGGAT6W6QWYNGRTW1NPE454J0VPJ3DKT19EA10N27P6MTM35HZZ8GJ8CGW06HGRXSZ5HJA3SQFDRZS2XN68PYM28918ERVV9BDVKFBMRK9NDMWMM79EK198",
+                                privateKey = 
"3V1SMYW447SGBHHZMZN8KDB21FYGNT0P2WK7A1XAR4AJYMXZNSGG",
+                                publicKey = 
"SVM02H0CDHXQX2FPR2B31ZZKMT0SQWE2S3VEWSAZDEDVSDDBXW00"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"XSW3C40PHH03SX6G74QJ9CJCS6RFA2Q1V79G4ZS0J1F055ETW5TG",
+                                coinEv =
+                                
"58FBPPVPMZEPMQB3RV4NJV7GA8GH8XYJ2NZ8N0NYG4M95434E1WT5GSGGM54X5X3FVTKKM5HRG28TCSD5ZCMC1V0KV6YTN1VEGQWVEN92M3TV761KWQDEJEZGHFKE7BMWP75KS37PV9SX4VABSEPS5VZCZB4C3RPG36QH2GH366G0AGQNX2F5T80SBS51Q3XA4FG44KJ14D32",
+                                privateKey = 
"8SFAB3CA3JQ0KB18MREYVJYJYQ0BNYS1NCVPKHW7WS3YB4QN898G",
+                                publicKey = 
"4QYR2AQT98J5QNMB44P3ZVFA0WVHJJTWG7WTXRKYJ11X1FYNYDGG"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"Y94ST132M2FTD579X8V9E3SK349CZQ5Z70RZ29VSP8S1PM08ZV30",
+                                coinEv =
+                                
"HF7CMN9F3Q6EG85C10SQ313336MFQTWJ9ZQ85J4EKWX6Y2546ZNQCPMKDRM2E0KZWQFNR57SKE7EHJ4BWVT14582MT1YG0ZA7RC2DX5VGZSWVT3N568TNSN9TPKYD74KMAH2A0SSXY8QNXF9C3TQD0HPQ409FNV0QR4R65PJ0G4AJAJW3EH6W4CMEXRVXSD8DYT81H6KFPR12",
+                                privateKey = 
"CTNN080C67WSNA96SY3ECCGYM017T3SYZXTJ3CTDEC23E1ZCXVSG",
+                                publicKey = 
"R4G1DHWQCB8MS0E5ET8YTC9M9425R6Z146HKM1ADGQJ0Z2QA1YQ0"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"2P1C6Z5RJNWPP2YW4SXGED45AM9H4EM7M063V8HKVSRHA4HGC5A0",
+                                coinEv =
+                                
"3N07NHNBBSZMQP04EPSN3SC8DZXW0DHWGZ6QD7A20EWD8DDJ00PB2CH11KZP404ZXN752X4TMA6D09C459WXYQDBYQJ70J6920MJ63P9XRMKYHEKWRSJEEC3FX487VFMJJ8ZYYBR3C4YRVPADRXFPZ8T7YJ4667TJKCSRQJGS7V2DS953HQ509M9ZHHWRH19XBFPTET3R71ZT",
+                                privateKey = 
"DWNV6AD7WKZMMPG342D1KB9PMH6D8RW2ZE5T0MB07KNA88NR2RF0",
+                                publicKey = 
"21STR11NETSM4VVBZY65NEDNJHXMV27GM1NP2P47DJN4C8AQS21G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"XTRQX3ZCR6MAKY2AJR2KF9C4GJZ0W5HKK71Y0EHG683WBG6VFHQ0",
+                                coinEv =
+                                
"867475JHJNY8BWHQH23CTCF3GEZZ2R1JBFKER7FTFQFNA9RCG4JR8Y89165HCW6GZQFBCWN8DQ91V8G3JH9JG4RPNJ6YZ6K1R2JNN4H99AR6DR96SDQ5N8YZSKFBFKD2QE8E9KAD8K05AVXPPQ4R4NDN45R7YTA346S1PB24GR0SCG7TPS1W59596KN3V1F4G17RGAKQA66T0",
+                                privateKey = 
"T90M7DVZ78WVVDHBGTHW0Y38M88AWM48Q8MEP6WXXK4WGQQBF0C0",
+                                publicKey = 
"K1MHGCPZSQ8HY8T8WJSAXE5DXH5SMXAGTZ8YZYFWE1ZYBYRHPG6G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"BM2NA21HG2A50GFXZQCQKK5Z23J0Y63SNM966M2FHDPM2XBYJND0",
+                                coinEv =
+                                
"GAZ5VA6AD060FE466W5DHJK8E5TBY4MY6TEFD0TZ9BX45CT4S9V5NGV47G26ESW0Q31787GX1QB756J55BNQJY1SASN2942EARZTYCWC9MEA0MAGDFR6S5N6QRNFEACCHE37S4DB2DN4HWKEJP8JM8RADC73KHCBKBVKAEZGYAFD4P99BSER0S9S2ZH7GAWQYZ45YZY7W6C64",
+                                privateKey = 
"GY4ZPR1BJB5YY0273H46GJTS4HXTWEFPZA4KT5E3Y9H12R8TQCN0",
+                                publicKey = 
"9ZXVR8Y0TDT2H57M4XZ0HGG1R78RBSN3KZK2WAH678G09XP2WM6G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"X24KD0GD1GC8JWDBB72R94XJMD8FKR7T2GK1MX8DFJN0PY6EKH20",
+                                coinEv =
+                                
"5GTW52KR7ACEGQ96SB92KMYBCC8XNPCGV9Q8ZS7MT1N27BH1NYM3BRTR8XJWZZ5FQ5KXEMT3FA374BQTZVPS9GTYHX9J3NMRSBJ6XJA6AGZDMW5VDXS3QWK49271WH1CSHMG2ZJ7XM0NJF86EZN7MPXKTV3WPD25AZ0EA4P980XB7DZ8GA04CVET8K0VFFP4KB13N4088KEB6",
+                                privateKey = 
"K1W5XB7SESBYTAPP4PCS9DNYY4Z3HQ0464BW4T15VDZ64GFCXBZG",
+                                publicKey = 
"W88CW2E3Y4Z0VZC8KE9Z4G6H2B8E4BCRGNDPVVD6MF8PNGRHGMH0"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"CF2AE3C5HVQJSP0R7CBBTAYQK1NDTG5DTXGW6D6KKWG3053QMHX0",
+                                coinEv =
+                                
"FWA6FJY0YM5HWA96QQNH2CGTTD0NR6DTN5YNRDZCBHDTE4A3ZJ5793175J6G0QRPTHNKEMPJFSQEVB4QH7MAHTPSXRJC28WEQ9Q9C7H3R5CDAE9SNMB61RXWV1D1JH195E13690RYZP62KC9349Q25JHADQ4Q4JQJE4MNJK19H3T1WHES285NHCP8BEE3MSE4TH567P1CBK58",
+                                privateKey = 
"7ANNBTWEQPXP211W3TEHWTNRFG5ZFSCW393W0HG6N24PJN6XK6PG",
+                                publicKey = 
"2DJ9HFE9HBQ0N8XNEH6BQD7R1H81BJD40Z94FVD016HEHT8JPCRG"
+                            )
+                        ),
+                        listOf(
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"J5979NKRJE2PS2NSVS4T4D6D1CSVJF7TBHKXA2FQYEZF6332PV2G",
+                                coinEv =
+                                
"8S4CVYXJNHAQ93Z2ENGKJXNGYEXXMKE89KXH1TVRGVEY9PJ1NGFGENCKWZR1ZFC8W0TFXDGPYEAPHVPJAY0PT99G6GNJKDYV0YC89XGZK4F5HGN92BY7HT04AF2HY1RED8B0KKRKX9RV727XSV5QG9TZFBSAP4Y4HXZKEN1DEBRR2WJFYSZ2MYV50XPK5T7TPPMM30TX4PX88",
+                                privateKey = 
"CJ6QMHX606JYZY9DS5VGPTFKDYYASAD60MFRBZTVEDQQRYX2KX2G",
+                                publicKey = 
"WYYT5DM2QASGBCTSQKVXT5M7MREXV39MDF11HM5JX4NKMNXWY41G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"BJ9N8QGX1VYYRP1RR4WZGE2NMKY1EG3K6636HPY624MHT1FNK1T0",
+                                coinEv =
+                                
"J5XPY46YX3S314FSBJD1MTZXCT87Y9PYKY17TJP5PCJNK2RF5JGMWBJVJQRXZCJCCFPY3FH4R7TD9A3FYB6JF4Y5QNM8Z46M90599H3HP2BSFZM4HW68R5DKPRE11GADR6BAW7ECFYAREDBZJNC5C18BMZWHP5XZ35CZZRWZ4GG1910KXX6A3789KW1028VBC46PHC3M61396",
+                                privateKey = 
"FE0T9EZM72EZA5PX3TQPVNM9RM5NWJX4M9CEVFXW2BF2HF28AJ7G",
+                                publicKey = 
"2Z07PKFFWXX53NXPX76HBF5RYRRM6E41A5KD5484VJ2DB7P64AA0"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"4B9D3MG4TEHDCETMKBAC3XGYW7A4FFWQ8MEHT7V5SXC0BY0AF3AG",
+                                coinEv =
+                                
"CX96G0RTN9N3QFYNKFFWG165XDKYW7FJEMB5K9SKKYB6A9DCK33T34VDDAJBH9VGG6ADXAM2DBVXZYPVKST9VK2KN943A9MRK0HNP8JVEJK84H9CVA4D1Y9N1351V05H2NC8TEDG43CS6RHSD37HX08N3Q03EW887GF2HF97MW23WH9CH2HBBNS9683DH7K5PY6H3MSQZE0S4",
+                                privateKey = 
"ZS5031P7N3T3B52GNYGF7AEQ44HVCV63Y5Z0Q8E9XME6JC4VGQVG",
+                                publicKey = 
"QB9HPVS5HXP0TVCACT7YB8B2Z7T9NZD47NA0XR49ZBCKJ03M7NXG"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"VTT1W5TK7JTTC6NJZJBQQGD6D005JKXXRGCWRG7AQ6Y86D0JPJHG",
+                                coinEv =
+                                
"AGZ8ZKH7HJ2E4QBQVKH0SH94SPZFSQYG1YG0NFSE7T8HB7P6JKPF66ERSFGDH1CYRBJXA0WA1GFPX7WG528NBWXVEAN9HERZZ45XV2F6H4GPTT59F4TK27VK01NRXR93G2NYSS2V3FVVTNNTVB1BE57S5J18J2TD67RMYWWFCG0S1JPXMN196QBKBMB2KQNFP05G9WBPGH4ZT",
+                                privateKey = 
"MXXTAHN3VWEPD3327H55S8F4JMZHBFFDDV9FBK4SQH8SR0TRRWG0",
+                                publicKey = 
"XZ4VZMRZMZ32Q3DPPSPFHST8D41MX5YFT6BSF51C0D7M30M16NW0"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"047V0KJ23HSGECQRR4MF8DRN3XT3HTHZ0QV585PBJ4HG75CYCPK0",
+                                coinEv =
+                                
"DR1K7Y41WADGP405ZZTCPAWBS6QS9WZEW6VX3JGK1KHYNWZKREKA375TXZM73X558449PR805JCECYQNPCQHZ7HG7FNBT8DEDRRNP1JKJX0HPVTD155K1ZNVWWP2YY9THNG8729MKY3Q77CD7NN8MENJYH9E5RA4FXQYYEGQDDPG2YS0TM595HNA2KX1TD7KYYJ86AGT7FX7M",
+                                privateKey = 
"C5M9VGNMQ2RM92DA92417HA5WGBTP2R2DPZWB6E5MA61S2TNX7W0",
+                                publicKey = 
"JR43Q0EGAH85B2F1VQDGHZ33318FGB44158TC0ZB4YP8XF1N3KYG"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"B4DSQ4BZP3B1QPG21ME2R3BFE0DPX4GRSAVHF3S0V1YEG0JJX620",
+                                coinEv =
+                                
"GAH89K67PPPNXZMC8Q7KEA9VHXDV1WB6WHSJXWB0852B4G5FECGAG9W36NV13AYMX7N37Y5X0NZ3WWKJHFKY8R328EEG528KPFV7E7NCJYZZB83PA6DDP5ZVAQF0N79WDANNEXRZEFT1H8CW0ZFK7533TR7FRTCDT9EWRVBDRJ23NX45C6CQVA6CK1JN9P0ZXP69FPJJAYHHE",
+                                privateKey = 
"A0WS0Z3C9RGJNX9TX5QV4KYYVCTVNMW4N3FD10W97X6AF28KVB10",
+                                publicKey = 
"B5MBRBBTF6NR53QPFJE5X8WX8GG9Z4TEDG2PCPPVNNB6XHHBNRF0"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"ZTQHYH1RPX4XEJV6HG50EQZF6H6M74AY75N3DN4G81ENRS38AM40",
+                                coinEv =
+                                
"FKH1C38XNYGYXK0RMMCVJHJAM60JA0M7T0XBVNV0K580M4VEW6SCR528BV65C7J3XYEZHCGMC1822J4AQXT5KS492KNB8G6MCJYJ95FMRD6R47PGWXSBH35JS95WSKE72FSB5W3Y12D6K50D5H0B7SKDQWAK4VZXNHC1S1X01KHV6KVZCTJW763K68ZABAD9R2TQYB686GE8W",
+                                privateKey = 
"AQZAJRZV4R2FK1ASYEXNJ9E1K3QV0PHSBZNNPZGK2TR2QSBW4XPG",
+                                publicKey = 
"N53YEANM052FMJZQDJGWGWERVB9X67VDRMA67X31Q7HKXFMJTJVG"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"GHJ6XF1XWYY623QDYNDXKTZ0PRSHAN41H8SHA4RGD1HCTV9PV570",
+                                coinEv =
+                                
"3ZP6QZ4X6RH1R7C60T51P2HQEYZ4RMFG45MN47J6NQ6YC1M9M2BAFAQBM87JD8T1M69002E5QD2QZDV8HDJ492CT107TWZCE4TQ367GDSTPT6AAFS0JRSDJPBNME5KD6ZH8Q42KNMQ4PCM7GWA2ZQK7V35Z7BDJGWRFF65QGEPBC3F8PKVC92KJ8AN89ANY9H4YM55JFDCRSE",
+                                privateKey = 
"T7BME1SM7Q6YEKNS6CCEGA7Q54SB1D451H1TJYYNGV6393R2QKGG",
+                                publicKey = 
"QBTQNQKCGS6CH2W6AQSDSA7M1EFF0DC5Q1AE4MK2BQM0EBG3RJQ0"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"1KFVREMW5KJZ3YS65MKCM40GNTCYBREPN7KM26YTVDW0GRPSDYTG",
+                                coinEv =
+                                
"K08B7KDNK8C179M042GZ6BYGRNEK2M3YKTR25FZQ1H9AF9EG9GYFEP6Z2KZ3FH1SEPWYWHNQM1N3507PG99B8XWMR1ZG8BR1CFFM2EQFB5W8SVF145NS7SYXSDPMJ7A64NJZEQMZH0VS2XY262M3BDMVWP7FDEG3W1XN4V0S6GXA14GWRNKFG5MYX8RBPFC1ZHPNFHRGD1FT2",
+                                privateKey = 
"0Q8W768BQYAPSQCD46R3GMH7QAVT6ZJ0T5YA2A3WXXGRGX7X2WGG",
+                                publicKey = 
"AK1YSV2A9Y61E1XV313KXR1CJY81RZDKZW4TK9CZ82KK7YFJQDY0"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"M2FEDXEYNBXQMMWYR36QY9TSVFYW910947RKFBN4AQ3GNZW7565G",
+                                coinEv =
+                                
"3R65GCTT7EB685EGVXCJE928KPX1HH8YNHKD3JYYRJ26PD066GG21ZEE5HXSFX9D22NH31KMK4SPKB9QKNAWK539BYSP58ZZ9W011FYTGMKMDB8CMA354JMH03B814Y4MM64EE5BA1BDJ5BDZ0X6CT5BZ51QMD8NBZTKR1SE6YF1YZ6RJDQAG12E312F2VRZNYKXHGXNDSR1J",
+                                privateKey = 
"V6XNMA56RBR8VRK0GG6Z76STRA6QMNSJZYB15SFS7XX1QPFNN6KG",
+                                publicKey = 
"KM59YNG5ZZH9RHCN2XF4MY9YEGXC4A7HHN907FGG9C4W6ACFNY20"
+                            )
+                        ),
+                        listOf(
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"X84BZ3AZ0KMG3BYVRT3YT04ESCB7R3YEWVBZN5ZDDZ6WN00AEPR0",
+                                coinEv =
+                                
"3B5ZHJ3NESVQ31QEBJXAGF6B5SJBEMXX5Q2FDTHRW807FDVQ3Y7T7KVQ5JSMB1DA3EQCQDNJYVZDQQQDHHHDR5TDEZZDJHX9S94KEKTRMNJDPFXSYABCCMWR7R4H1R7W6TM5Q4B5H2FYZXAVCAJJMC13ZK53N83SRRF8EVAJ2ZB51D6X7WPWQ1MQX3DPTXVZV4EGGPMYDP720",
+                                privateKey = 
"FFZ1E4KGF8Z6HHYGQC1QR6RJFDQ3QRPCDM58NMK6YTFTSK6GTA9G",
+                                publicKey = 
"Y7F33NG04MEFAK0QCBAHG325705B1326HCJ9WFXQCZVKMJJ8ZE30"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"E3QTYEC6NNZBQ6QTSX7TJ50CQGM3QCBABVPDSZANHFJNX8CW2Q90",
+                                coinEv =
+                                
"6NE9SY9SZXTCJP4SW01T5R3YYZFCEG7AR5VJ4JNVQ9WPB7A1DGVXDZQ68233KSG5GEBV4W4T74CK5AC2MDKST7RCJVFSVP1B4EETSDVNWSJ8VF2V7BY8W9S85Z06WJE223PW692CEE4JA0Y83Z40Z69P28NS430BX9VMWRXMZMCH9FKB92TCVZGTXM3CJ2ZG5RRPNZB60VKKT",
+                                privateKey = 
"ABAF6S5WZMZZ1ZWXJG7JVEQTMJB5ZBRR4T554Q0SNDZDRFG9MNMG",
+                                publicKey = 
"HFVN1JANX4A4DSB4K458255GC5S879QTSBF2DDEM2K0HPZQPHF0G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"VRWF9B40EHKB4NZ6FXQG1P26XESD7FZXSEA9X53DZ1WVXS22GZAG",
+                                coinEv =
+                                
"EWSVHPMDEBPW256A7K20TSW2T9EBQNKCHQWRJ1HJ85NW9K058H9C5C8QZ1R2ZEKDRYSEMPM920HXSE863BG8VKJHDR1ADQFREWH6TA2QA2ZANSMNJDHCA1EMG78A9PSMZCYE8Y82SY70MJSKZG9ZF0F5EG3KJ14X21RC6HY41P79NVETG4R11GF7MADSTF11G5CNB8EX26RN8",
+                                privateKey = 
"DKHBJ9SRVEGAD7Q6K7PMH8DMA8WSRCK31PEAD6NWA4T6QRCMBW60",
+                                publicKey = 
"DMVWT6WPYWHHX4G0H124HDQ6QDA1T447WQQW9NVR1ABJZ31E3YA0"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"7HJN1EJK12M3JF7H6J6A82X9VB0VWCAC5MW4DA0W7524ZA620F3G",
+                                coinEv =
+                                
"5YFPXNJ0SKNBJ6ATMD4JAPCMDFQD25RWYYQZXM7X6W8V9VBVBRT4M94MTMSE6W5H56XMCT5MF51MJVM9W090223R5VWTDQ9KEAHT45VPASHXNJ817R1A2S50F7TZTDHKE3ZKPJ1ZSTC9XDS57N0JGZ4AK3606PZ5211G8BDWSAGY8N0JYNASZRZ9JABN4HNTKRXRYZKN0V240",
+                                privateKey = 
"AXZTP48HT58DB4BFKREZ34ARBVREC1BECK0KEYBWFWVP7K17ZQG0",
+                                publicKey = 
"HP8PY0NPT0NV32RNMEYZ63YSDYB2T9J64KXAYV76M1N9Z4GPF8YG"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"KK0VT0E3KMHG39VT0B5NNTEXC9HMT9HJ6554THEVWHPHS96H4FS0",
+                                coinEv =
+                                
"EMNAXG036TG4JNZ2TWBE688THQ1BZW8H5PCYMD4Z71K5FHSB6N8QE54V03RK0GJQVCDYPH699PPR5HDSWCBV13YG6WX9DX097VPMKTHSD7CYGH611RGDJ3KJXJQXQCDYXC869JZ9ZTBNGYGVZDVP16141Q32ARRF59T0MDKR6C4BJQ1V32Y2ZWHRD5QVRBAYS90KPDW6G00D6",
+                                privateKey = 
"4NFWW24DP7V17ZN4D12NW6TENEMF54XZMNB4XVXM1Q31AMC8DKHG",
+                                publicKey = 
"A7F9SY8413B3EQSVCES7AFDYRJH768J8AA8DVDR4BR06SHXT0W1G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"1KB9TCM2GDT61ARG99NA5TCG35H3P4K6ZGXEQA33P3XNQPSX33G0",
+                                coinEv =
+                                
"1MRTJBESYQ1MSJC117JHRT90GS7WPCF5ACA198RGV4X0E464W2KMPFS4K43Z94NJ8QKP40YZCRX22ZR23MMCFWGNS5KYDV57JH4VZM6YSDVCRDGCBQDB87BN9TS2PXK8GXK3JA3G84X5HBGY7CX870B3MWBHW0V6FHMQNDXVFP1PCYZT0T0GAPPQ02MZ6Q8JGMSXT0QFXF9P4",
+                                privateKey = 
"289K9AW8M3N45RZZNGEYBFHZEGCTKRY9TB15F5SNVAQ09BHKHEWG",
+                                publicKey = 
"BZFET2N4MFYPQSMDASD7DJHA0EC8KGSB8RS3G34G8B5D6S9W1B90"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"B28XC87F006EE1B0GRMBEW3P88J324C7BYPS3SCVHAB3KRA7JBM0",
+                                coinEv =
+                                
"8S771KDCPD9XFRXDT7938T8HX2XNGM7FH3WJGTYBD343GY5E427ZPD0QA4XAJYWN9038K0BH6Y0R2G42T8PCAM4W0YP31FPCABPZXPCXZP52ZDB6QR3ZG01KS9GBWAJ4PPKJG24MREBVTX37R90WA44G7Y8G5FCDBEQR1TZ3PZ0Q4C3K0Z3QP1HNNEH3WEP2QPFJD82YFF1G2",
+                                privateKey = 
"592P3HWA2D245M1ZHA4AMSRK6Y5A9YFVP70VAKBAF6GSVVZE7MW0",
+                                publicKey = 
"VS7N76T06X9GPEPGF1K7CD3ZTCJ4R8DJW715NRE30HT92JXQ076G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"9VQATS7SQ9QRM19602PN3QF16JEF9FM8GT1BZC8WRM9Y6HRV9HMG",
+                                coinEv =
+                                
"CMZK656TCB6BRDRXVZ625SYZTGVKX56J77NE9B6T0EHRM8DRD7MR5WDRWQV8Z5WWDTAGKPCPA3XHCTGW5T156KK9RM340N53VPYJTDXH22CKY68PY8J4J40XG40YZW3F6TREE5TRFF51VJT7QV8CHW9PM67ZK2ZZWGVXV3KJF2HVDYFS2X8BHC19B266H5BK5EX6NJ65E9ADG",
+                                privateKey = 
"4QVMP2V1FJQKTCTVXFBDCCR29T3BG40RCFZCJDPG6WPAS89EWWF0",
+                                publicKey = 
"MV5RPV33Z2EFXSA6XPV0GVP2WBAP580JXAB2WD0G86FM29VM2X9G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"ZTKWFCA58WC7GPYDYBMAXCH12KPTRGTXR2WMYSX8HPSAZ4M47480",
+                                coinEv =
+                                
"7Z9T151XYF58P4T8B7M3DS8VYFS7MXQPYG40V1JZ0SXXFNVQ1E6GX6C64F36FQADNPDB7S4YDNR80TGCACPXD5ZEP7JQ514Z1ZS43D739BBGPWH5QHGGDN91V2KDSDD8AS4V2M56CF26G0RBVKV8EZTM6ATQCQBVJE6Y4S98VP31WECYSW8RAYKHMHWKN5DHF1NMZMY86KA5E",
+                                privateKey = 
"M87WJ5ECSYVVGK0PDTWPJCQS5ZE27D79RAZVGNDB8HWGC4R0X1G0",
+                                publicKey = 
"7ANMTX5E74K8074Z50E36D8QX5J9TQ6B7WPN7XK3DPV4YFS1DX7G"
+                            ),
+                            RefreshPlanchetRecord(
+                                blindingKey = 
"2522XN7JAFCVXSFMG031CSA20DFMY2MF5ZTZT6W3HP59RPW5S34G",
+                                coinEv =
+                                
"5R0DF70BTV34K5X8DQG8Q1XY55RXCRQ8KF2WK12QP90DBY2QRWF5CJDE20X5BFWD5YKWVMKHV8QWE6SFGSHDBSJADYKA7WZXGBJ2EJPAYXEHMTTJ89KH60KZME1SDBBYZA239M98JV56ZAMZPNMG928Y4P27F5BPBSGQK3E3A700BDNW1TTJ5VYPXN8SNJANW74YFPJZN2HWY",
+                                privateKey = 
"ZAX0KQPGATJT5EMWP7D6G4TB8GH6ZS6C17PJ91FS4F4VWX16H26G",
+                                publicKey = 
"DNYGRGC1R41AH28FY063N71RDYPFYMDB20N491R8297R33G72FE0"
+                            )
+                        )
+                    ),
+                    transferPrivateKeys = listOf(
+                        "KXR71TDT7A5GBZ6D92PWYC7PY9Q18KMVC4F4VRNYZ4NQ2V6DDY50",
+                        "DWRWHG4NN81SWFYVJ1B1EKN9RSXRBJYV06Z8SQWWWTN6W6TRPCEG",
+                        "3ZSZTTF3S1BG4MDW1GF8PE5EX2A2WD6WBTR8SYSD6GZBS4D4Z780"
+                    ),
+                    transferPublicKeys = listOf(
+                        "2KQHHDF90PETJ852EX67W441PGZ5M47ZRD8XT0DY0HGY5Y1XWXSG",
+                        "ANNVKXH143SB5EXDMS9XN2NXV0ETVA7KFJPS94Y6356H16K0JH5G",
+                        "JB60PMEWBJ0Z51QGBHNHVQ51V8FT0C97QX64DFSK8SXEQNSYV1EG"
+                    ),
+                    amountRefreshOutput = Amount("TESTKUDOS", value = 1, 
fraction = 81000000),
+                    amountRefreshInput = Amount("TESTKUDOS", value = 1, 
fraction = 95000000),
+                    timestampCreated = Timestamp(1593697014952),
+                    finishedTimestamp = null
+                ),
+                kappaKeys = listOf(
+                    EcdheKeyPair(
+                        publicKey = 
Base32Crockford.decode("2KQHHDF90PETJ852EX67W441PGZ5M47ZRD8XT0DY0HGY5Y1XWXSG"),
+                        privateKey = 
Base32Crockford.decode("KXR71TDT7A5GBZ6D92PWYC7PY9Q18KMVC4F4VRNYZ4NQ2V6DDY50")
+                    ),
+                    EcdheKeyPair(
+                        publicKey = 
Base32Crockford.decode("ANNVKXH143SB5EXDMS9XN2NXV0ETVA7KFJPS94Y6356H16K0JH5G"),
+                        privateKey = 
Base32Crockford.decode("DWRWHG4NN81SWFYVJ1B1EKN9RSXRBJYV06Z8SQWWWTN6W6TRPCEG")
+                    ),
+                    EcdheKeyPair(
+                        publicKey = 
Base32Crockford.decode("JB60PMEWBJ0Z51QGBHNHVQ51V8FT0C97QX64DFSK8SXEQNSYV1EG"),
+                        privateKey = 
Base32Crockford.decode("3ZSZTTF3S1BG4MDW1GF8PE5EX2A2WD6WBTR8SYSD6GZBS4D4Z780")
+                    )
+                )
+            )
+        )
+        for (v in vectors) testRefreshVector(v)
+    }
+
+    private fun testRefreshVector(v: RefreshVector) {
+        val record =
+            refresh.createRefreshSession("example.org", v.meltCoin, v.meltFee, 
v.newCoinDenominations, v.kappa) {
+                v.kappaKeys[it]
+            }
+        // use expected timestamp, so we don't need to mock system clock
+        assertTrue(v.refreshSessionRecord.timestampCreated.ms < 
record.timestampCreated.ms)
+        assertEquals(v.refreshSessionRecord, record.copy(timestampCreated = 
v.refreshSessionRecord.timestampCreated))
+    }
+
+    private class SignCoinLinkVector(
+        val oldCoinPrivateKey: String,
+        val newDenomHash: String,
+        val oldCoinPublicKey: String,
+        val transferPublicKey: String,
+        val coinEv: String,
+        val signature: String
+    )
+
+    @Test
+    fun testSignCoinLink() {
+        val vectors = listOf(
+            SignCoinLinkVector(
+                oldCoinPrivateKey = 
"884XH9DBQT9MA34YKCR7WPEP2HH3J1R9C3G7MSB8C3NHB0XEVZW0",
+                newDenomHash = 
"ZVZMM6GEFETH1S71BRKFKB03BTSTA8JZH26Q1PZED78NZ50EVVT0DMDBPDN5EBY90SK4AWT0J8CNPD7NB2TSCFMSKAS5V6DK26GEKT0",
+                oldCoinPublicKey = 
"3GSHK9JCTMEAZY7BMC1QQD28ABEJEJJJXHPVF2YXTET97WB81GNG",
+                transferPublicKey = 
"ANNVKXH143SB5EXDMS9XN2NXV0ETVA7KFJPS94Y6356H16K0JH5G",
+                coinEv = 
"8S4CVYXJNHAQ93Z2ENGKJXNGYEXXMKE89KXH1TVRGVEY9PJ1NGFGENCKWZR1ZFC8W0TFXDGPYEAPHVPJAY0PT99G6GNJKDYV0YC89XGZK4F5HGN92BY7HT04AF2HY1RED8B0KKRKX9RV727XSV5QG9TZFBSAP4Y4HXZKEN1DEBRR2WJFYSZ2MYV50XPK5T7TPPMM30TX4PX88",
+                signature = 
"H1V8D30JTVW8F7KERCWRDHPVVV6BZXNE0QG4V7ANEPTVFT2NKEWCCRM8NSG6D6MBA80ZSSYG1KYZXR8CWG0HV6N7QA48C4YEWK4M030"
+            ),
+            SignCoinLinkVector(
+                oldCoinPrivateKey = 
"884XH9DBQT9MA34YKCR7WPEP2HH3J1R9C3G7MSB8C3NHB0XEVZW0",
+                newDenomHash = 
"4VEW03Q1JRQTKDGDKV73DX4RYHPDMPYQ48BZWAFVFPB03XK9HNFKWM1KZCR0BHXGS0034W12CE1VG1J2YEN4G7C1400MGX35GDS263R",
+                oldCoinPublicKey = 
"3GSHK9JCTMEAZY7BMC1QQD28ABEJEJJJXHPVF2YXTET97WB81GNG",
+                transferPublicKey = 
"ANNVKXH143SB5EXDMS9XN2NXV0ETVA7KFJPS94Y6356H16K0JH5G",
+                coinEv = 
"CX96G0RTN9N3QFYNKFFWG165XDKYW7FJEMB5K9SKKYB6A9DCK33T34VDDAJBH9VGG6ADXAM2DBVXZYPVKST9VK2KN943A9MRK0HNP8JVEJK84H9CVA4D1Y9N1351V05H2NC8TEDG43CS6RHSD37HX08N3Q03EW887GF2HF97MW23WH9CH2HBBNS9683DH7K5PY6H3MSQZE0S4",
+                signature = 
"90NEPFDDK99524R9RZ3H4NEK6S5N71CQN6DAXJ9S87YASHB0JYG3VJKSV7BVJVCRCHWDYYS61CXQ4XWW9DMS6HAV0PAPWEY7FPKB430"
+            ),
+            SignCoinLinkVector(
+                oldCoinPrivateKey = 
"8G7F1X5EQV9P7JK1YVY5MFYX18Z85FRQFCKFQ9J5HX8B03QEAR80",
+                newDenomHash = 
"HDYHXFJ65YSW7BEVCP1G282E4QHQ9KSF8KBQNM4JN4T3KD30YVN2G6G1HFWEMSHJGAW1VM6N8HDEYRXVYJ3A16C58WM6QATMENGJ5CR",
+                oldCoinPublicKey = 
"4670V0PA3JG928CJFQHGJAC3DKCPJR3HMRRYHKAHTEEGQDK78HAG",
+                transferPublicKey = 
"BH4PSJF3T0S9QNFARDXTBWXWT46AX5CH9JENP5D1HQVTNK9YXN7G",
+                coinEv = 
"PDGXW6PDZTFEGMYFCVW972V4WR9WXTYW6JBDCW52DCEPCECBK5AX8R276Q5JZ38ABQ0X0E8NSSV28H56CX3MR3YTXFAJKHY9M93EECWDCK6AA4259M6DYSS1D4AMQ2PQJH3Q0BFFXDS9X5Z6VKFM0EH05KM91SBB6679NC2SF0B4A1G2N5KTQEXCQADNJKHGGZJ704PBPYCGR",
+                signature = 
"7STWC1GZ6WWYBYWBNA1BDKFYAX8TCRGFP36A3W0TH72K51R9DXMVRA2H6N21CSA40Z3SQJ0NCFBNTR0781PX3027QX7KZ9KC7Q3SW20"
+            ),
+            SignCoinLinkVector(
+                oldCoinPrivateKey = 
"8G7F1X5EQV9P7JK1YVY5MFYX18Z85FRQFCKFQ9J5HX8B03QEAR80",
+                newDenomHash = 
"Y7N6C15W0ADR06Q2QKK15VWKV09FMHC0BHT0ZZ95FP8T7G7T2GCYWVMYRE605QCP7NQ02QYH3Y9V834G37K8KT7V3AY75N715AYX170",
+                oldCoinPublicKey = 
"4670V0PA3JG928CJFQHGJAC3DKCPJR3HMRRYHKAHTEEGQDK78HAG",
+                transferPublicKey = 
"BH4PSJF3T0S9QNFARDXTBWXWT46AX5CH9JENP5D1HQVTNK9YXN7G",
+                coinEv = 
"63X0T7DWHJHMKR7GX4D9CTNHVMGRKBN4C2VHBSNY6W8JMJYJNDD40XZXG9RB7V4W7HHJ605XK6R9VSBMJ8WCF5AQ3A0631F6MFTSEKTKJDMMP8006DGZ0XMW43JD0PD2XXX42HPAD6DY0ST941NYKNMA050ZMTV1JKNYW9495RVXQCMKPJ759KCC4BA6066E17Q7AVNS6EJD8",
+                signature = 
"5NVY7F9ZBSHY0JP1Q8JFTJYCY63QRT2JNV3R25050451ACYDXZ4YT0CE3TQYAECMVZ3F092GN7TQFNJF3G24JZN6T7XHDDG4MT0M82R"
+            )
+        )
+        for (v in vectors) testSignCoinLinkVector(v)
+    }
+
+    private fun testSignCoinLinkVector(v: SignCoinLinkVector) {
+        val signature =
+            refresh.signCoinLink(v.oldCoinPrivateKey, v.newDenomHash, 
v.oldCoinPublicKey, v.transferPublicKey, v.coinEv)
+        assertEquals(v.signature, signature)
+    }
+
+}
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/Timestamp.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/Timestamp.kt
index fbd6c65..c0efbac 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/Timestamp.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/Timestamp.kt
@@ -1,5 +1,6 @@
 package net.taler.wallet.kotlin
 
+import com.soywiz.klock.DateTime
 import net.taler.wallet.kotlin.crypto.CryptoImpl.Companion.toByteArray
 
 
@@ -10,6 +11,7 @@ class Timestamp(
 
     companion object {
         const val NEVER: Long = -1
+        fun now(): Timestamp = Timestamp(DateTime.now().unixMillisLong)
     }
 
     /**
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Refresh.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Refresh.kt
new file mode 100644
index 0000000..4ffb3b9
--- /dev/null
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Refresh.kt
@@ -0,0 +1,256 @@
+package net.taler.wallet.kotlin.crypto
+
+import net.taler.wallet.kotlin.Amount
+import net.taler.wallet.kotlin.Base32Crockford
+import net.taler.wallet.kotlin.CoinRecord
+import net.taler.wallet.kotlin.DenominationRecord
+import net.taler.wallet.kotlin.Timestamp
+import net.taler.wallet.kotlin.crypto.Signature.Companion.WALLET_COIN_LINK
+import net.taler.wallet.kotlin.crypto.Signature.Companion.WALLET_COIN_MELT
+import net.taler.wallet.kotlin.crypto.Signature.PurposeBuilder
+
+internal class Refresh(private val crypto: Crypto) {
+
+    data class RefreshSessionRecord(
+
+        /**
+         * Public key that's being melted in this session.
+         */
+        val meltCoinPub: String,
+
+        /**
+         * How much of the coin's value is melted away with this refresh 
session?
+         */
+        val amountRefreshInput: Amount,
+
+        /**
+         * Sum of the value of denominations we want to withdraw in this 
session, without fees.
+         */
+        val amountRefreshOutput: Amount,
+
+        /**
+         * Signature to confirm the melting.
+         */
+        val confirmSig: String,
+
+        /**
+         * Hashed denominations of the newly requested coins.
+         */
+        val newDenominationHashes: List<String>,
+
+        /**
+         * Denominations of the newly requested coins.
+         */
+        val newDenominations: List<String>,
+
+        /**
+         * Planchets for each cut-and-choose instance.
+         */
+        val planchetsForGammas: List<List<RefreshPlanchetRecord>>,
+
+        /**
+         * The transfer public keys, kappa of them.
+         */
+        val transferPublicKeys: List<String>,
+
+        /**
+         * Private keys for the transfer public keys.
+         */
+        val transferPrivateKeys: List<String>,
+
+        /**
+         * The no-reveal-index after we've done the melting.
+         */
+        val noRevealIndex: Int?,
+
+        /**
+         * Hash of the session.
+         */
+        val hash: String,
+
+        /**
+         * Timestamp when the refresh session finished.
+         */
+        val finishedTimestamp: Timestamp?,
+
+        /**
+         * When was this refresh session created?
+         */
+        val timestampCreated: Timestamp,
+
+        /**
+         * Base URL for the exchange we're doing the refresh with.
+         */
+        val exchangeBaseUrl: String
+    )
+
+    data class RefreshPlanchetRecord(
+        /**
+         * Public key for the coin.
+         */
+        val publicKey: String,
+        /**
+         * Private key for the coin.
+         */
+        val privateKey: String,
+        /**
+         * Blinded public key.
+         */
+        val coinEv: String,
+        /**
+         * Blinding key used.
+         */
+        val blindingKey: String
+    )
+
+    data class DenominationSelectionInfo(
+        val totalCoinValue: Amount,
+        val totalWithdrawCost: Amount,
+        val selectedDenominations: List<SelectedDenomination>
+    )
+
+    data class SelectedDenomination(val count: Int, val denominationRecord: 
DenominationRecord)
+
+    /**
+     * Create a new refresh session.
+     */
+    fun createRefreshSession(
+        exchangeBaseUrl: String,
+        meltCoin: CoinRecord,
+        meltFee: Amount,
+        newCoinDenominations: DenominationSelectionInfo,
+        kappa: Int = newCoinDenominations.selectedDenominations.size
+    ) : RefreshSessionRecord {
+        return createRefreshSession(exchangeBaseUrl, meltCoin, meltFee, 
newCoinDenominations, kappa) {
+            crypto.createEcdheKeyPair()
+        }
+    }
+
+    /**
+     * Create a new refresh session and allow to provide transfer key pairs 
for testing.
+     */
+    fun createRefreshSession(
+        exchangeBaseUrl: String,
+        meltCoin: CoinRecord,
+        meltFee: Amount,
+        newCoinDenominations: DenominationSelectionInfo,
+        kappa: Int = newCoinDenominations.selectedDenominations.size,
+        kappaKeys: (Int) -> EcdheKeyPair
+    ): RefreshSessionRecord {
+        val sessionHashState = crypto.getHashSha512State()
+
+        // create fresh transfer keys, one pair for each selected denomination 
(kappa-many)
+        val transferPublicKeys = ArrayList<String>()
+        val transferPrivateKeys = ArrayList<String>()
+        for (i in 0 until kappa) {
+            val transferKeyPair = kappaKeys(i)
+            sessionHashState.update(transferKeyPair.publicKey)
+            
transferPrivateKeys.add(Base32Crockford.encode(transferKeyPair.privateKey))
+            
transferPublicKeys.add(Base32Crockford.encode(transferKeyPair.publicKey))
+        }
+
+        // add denomination public keys to session hash
+        val newDenominations = ArrayList<String>()
+        val newDenominationHashes = ArrayList<String>()
+        for (selectedDenomination in 
newCoinDenominations.selectedDenominations) {
+            for (i in 0 until selectedDenomination.count) {
+                
newDenominations.add(selectedDenomination.denominationRecord.denomPub)
+                
newDenominationHashes.add(selectedDenomination.denominationRecord.denomPubHash)
+                
sessionHashState.update(Base32Crockford.decode(selectedDenomination.denominationRecord.denomPub))
+            }
+        }
+
+        // add public key of melted coin to session hash
+        sessionHashState.update(Base32Crockford.decode(meltCoin.coinPub))
+
+        // calculate total value with all fees and add to session hash
+        val (totalOutput, withdrawFee) = 
calculateOutputAndWithdrawFee(newCoinDenominations.selectedDenominations)
+        val valueWithFee = totalOutput + withdrawFee + meltFee
+        sessionHashState.update(valueWithFee.toByteArray())
+
+        val planchetsForGammas = ArrayList<ArrayList<RefreshPlanchetRecord>>()
+        for (i in 0 until kappa) {
+            val planchets = ArrayList<RefreshPlanchetRecord>()
+            for (selectedDenomination in 
newCoinDenominations.selectedDenominations) {
+                for (k in 0 until selectedDenomination.count) {
+                    val coinNumber = planchets.size
+                    val transferPrivateKey = 
Base32Crockford.decode(transferPrivateKeys[i])
+                    val oldCoinPub = Base32Crockford.decode(meltCoin.coinPub)
+                    val transferSecret = 
crypto.keyExchangeEcdheEddsa(transferPrivateKey, oldCoinPub)
+                    val fresh = crypto.setupRefreshPlanchet(transferSecret, 
coinNumber)
+                    val publicKeyHash = crypto.sha512(fresh.coinPublicKey)
+                    val denominationPub = 
Base32Crockford.decode(selectedDenomination.denominationRecord.denomPub)
+                    val ev = crypto.rsaBlind(publicKeyHash, fresh.bks, 
denominationPub)
+                    val planchet = RefreshPlanchetRecord(
+                        blindingKey = Base32Crockford.encode(fresh.bks),
+                        coinEv = Base32Crockford.encode(ev),
+                        privateKey = 
Base32Crockford.encode(fresh.coinPrivateKey),
+                        publicKey = Base32Crockford.encode(fresh.coinPublicKey)
+                    )
+                    planchets.add(planchet)
+                    sessionHashState.update(ev)
+                }
+            }
+            planchetsForGammas.add(planchets)
+        }
+
+        val sessionHash = sessionHashState.final()
+
+        // make a signature over sessionHash, value (again?), meltFee and 
meltCoin public key with meltCoin private key
+        val confirmData = PurposeBuilder(WALLET_COIN_MELT)
+            .put(sessionHash)
+            .put(valueWithFee.toByteArray())
+            .put(meltFee.toByteArray())
+            .put(Base32Crockford.decode(meltCoin.coinPub))
+            .build()
+        val confirmSignature = crypto.eddsaSign(confirmData, 
Base32Crockford.decode(meltCoin.coinPriv))
+
+        return RefreshSessionRecord(
+            confirmSig = Base32Crockford.encode(confirmSignature),
+            exchangeBaseUrl = exchangeBaseUrl,
+            hash = Base32Crockford.encode(sessionHash),
+            meltCoinPub = meltCoin.coinPub,
+            newDenominationHashes = newDenominationHashes,
+            newDenominations = newDenominations,
+            noRevealIndex = null,
+            planchetsForGammas = planchetsForGammas,
+            transferPrivateKeys = transferPrivateKeys,
+            transferPublicKeys = transferPublicKeys,
+            amountRefreshOutput = totalOutput,
+            amountRefreshInput = valueWithFee,
+            timestampCreated = Timestamp.now(),
+            finishedTimestamp = null
+        )
+    }
+
+    private fun calculateOutputAndWithdrawFee(selectedDenomination: 
List<SelectedDenomination>): Pair<Amount, Amount> {
+        val currency = 
selectedDenomination[0].denominationRecord.value.currency
+        var total = Amount.zero(currency)
+        var fee = Amount.zero(currency)
+        for (ncd in selectedDenomination) {
+            total += ncd.denominationRecord.value * ncd.count
+            fee += ncd.denominationRecord.feeWithdraw * ncd.count
+        }
+        return Pair(total, fee)
+    }
+
+    fun signCoinLink(
+        oldCoinPrivateKey: String,
+        newDenominationHash: String,
+        oldCoinPublicKey: String,
+        transferPublicKey: String,
+        coinEv: String
+    ): String {
+        val coinEvHash = crypto.sha512(Base32Crockford.decode(coinEv))
+        val coinLink = PurposeBuilder(WALLET_COIN_LINK)
+            .put(Base32Crockford.decode(newDenominationHash))
+            .put(Base32Crockford.decode(oldCoinPublicKey))
+            .put(Base32Crockford.decode(transferPublicKey))
+            .put(coinEvHash)
+            .build()
+        val coinPrivateKey = Base32Crockford.decode(oldCoinPrivateKey)
+        val sig = crypto.eddsaSign(coinLink, coinPrivateKey)
+        return Base32Crockford.encode(sig)
+    }
+
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]