<NME> BRFileService.h
<BEF> //
//  BRFileService.h
//  Core
//
//  Created by Richard Evers on 1/4/19.
//  Copyright © 2019 breadwallet LLC
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.

#ifndef BRFileService_h
#define BRFileService_h

#include <stdlib.h>
#include "BRSet.h"
#include "BRInt.h"

// Both Bitcoin and Ethereum Wallet Managers include the ability to save and load peers, block,
// transactions and logs (for Ethereum) to the file system.  But, they both implement the file
// operations independently.  Pull out the implementation into BRFileService.
//
// Allow each WalletManager (Bitcoin, Bitcash, Ethereum) to create their own BRFileService (storing
// stuff in a subdirectory specific to the manager+network+whatever for a given base path).  Allow
// each WalletManager to further define 'persistent types' (peers, blocks, etc) saved.  Allow for
// a versioning system (at least on read; everything gets written/saved w/ the latest version).
// Allow an upgrade path from existing IOS/Andriod Sqlite3 databases.
//
typedef struct BRFileServiceRecord *BRFileService;

/// A context used in callbacks
typedef void* BRFileServiceContext;

typedef enum {
    FILE_SERVICE_IMPL,              // generally a fatal condition
    FILE_SERVICE_UNIX,              // something in the file system (fopen, fwrite, ... errorred)
    FILE_SERVICE_SDB,               // something in the sqlite3 database
    FILE_SERVICE_ENTITY             // entity read/write (parse/serialize) error
} BRFileServiceErrorType;

typedef struct {
    BRFileServiceErrorType type;
    union {
        struct {
            const char *reason;
        } impl;

        struct {
            int error;
        } unix;

        struct {
            int code;  // sqlite3_status_code
        } sdb;

        struct {
            const char *type;
            const char *reason;
        } entity;
    } u;
} BRFileServiceError;

typedef void
(*BRFileServiceErrorHandler) (BRFileServiceContext context,
                              BRFileService fs,
                              BRFileServiceError error);

/// This *must* be the same fixed size type forever.  It is uint8_t.
typedef uint8_t BRFileServiceVersion;

/// TODO: There are limitations on `currency`, `network`, and `type`.
extern BRFileService
fileServiceCreate (const char *basePath,
                   const char *currency,
                   const char *network,
                   BRFileServiceContext context,
                   BRFileServiceErrorHandler handler);

extern void
fileServiceRelease (BRFileService fs);

extern void
fileServiceSetErrorHandler (BRFileService fs,
                            BRFileServiceContext context,
                            BRFileServiceErrorHandler handler);

/**
 * Load all entities of `type` adding each to `results`.  If there is an error then the
 * fileServices' error handler is invoked and 0 is returned
 *
 * @param fs The fileServie
 * @param results A BRSet within which to store the results.  The type stored in the BRSet must
 *     be consistent with type recovered from the file system.
 * @param type The type to restore
 * @param updateVersion If true (1) update old versions with newer ones.
 *
 * @return true (1) if success, false (0) otherwise;
 */
extern int
fileServiceLoad (BRFileService fs,
                 BRSet *results,
                 const char *type,   /* blocks, peers, transactions, logs, ... */
                 int updateVersion);

extern int  // 1 -> success, 0 -> failure
fileServiceSave (BRFileService fs,
                 const char *type,  /* block, peers, transactions, logs, ... */
                 const void *entity);     /* BRMerkleBlock*, BRTransaction, BREthereumTransaction, ... */

extern int
fileServiceRemove (BRFileService fs,
                   const char *type,
                   UInt256 identifier);

extern int
fileServiceReplace (BRFileService fs,
                    const char *type,
                    const void **entities,
extern int
fileServiceClearAll (BRFileService fs);

/**
 * A function type to produce an identifer from an entity.  The identifer must be constant for
 * a particulary entity through time.  The identifier is used to derive a filename (more generally
                          const char *type,
                          const void *entity);

/**
 * A function type to produce an identifer from an entity.  The identifer must be constant for
 * a particulary entity through time.  The identifier is used to derive a filename (more generally
 * a path and filename).
 */
typedef UInt256
(*BRFileServiceIdentifier) (BRFileServiceContext context,
                            BRFileService fs,
                            const void* entity);

/**
 * A function type to read an entity from a byte array.  You own the entity.
 */
typedef void*
(*BRFileServiceReader) (BRFileServiceContext context,
                        BRFileService fs,
                        uint8_t *bytes,
                        uint32_t bytesCount);

/**
 * A function type to write an entity to a byte array.  You own the byte array.
 */
typedef uint8_t*
(*BRFileServiceWriter) (BRFileServiceContext context,
                        BRFileService fs,
                        const void* entity,
                        uint32_t *bytesCount);

/// TODO: There is a limitation on `type`.

/**
 * Define a 'type', such as {block, peer, transaction, logs, etc}, that is to be stored in the
 * file system.
 *
 * @param fs the file service
 * @param type the type
 * @param context an arbitrary value to be passed to the type-specific functions.
 * @param version the entity version handled by the type-specific functions.
 * @param identifier the function that produces the identifier
 * @param reader the function the produces an entity from a byte array
 * @param writer the function that produces a byte array from an entity.
 *
 * @return true (1) if success, false (0) otherwise
 */
extern int
fileServiceDefineType (BRFileService fs,
                       const char *type,
                       BRFileServiceVersion version,
                       BRFileServiceContext context,
                       BRFileServiceIdentifier identifier,
                       BRFileServiceReader reader,
                       BRFileServiceWriter writer);

extern int
fileServiceDefineCurrentVersion (BRFileService fs,
                                 const char *type,
                                 BRFileServiceVersion version);

// Version limit can increase with maximum number of version, historically.
#define FILE_SERVICE_TYPE_SPECIFICATION_NUMBER_OF_VERSION_LIMIT   (5)

typedef struct {
    const char *type;
    BRFileServiceVersion defaultVersion;
    size_t versionsCount;
    struct {
        BRFileServiceVersion version;
        BRFileServiceIdentifier identifier;
        BRFileServiceReader reader;
        BRFileServiceWriter writer;
    } versions [FILE_SERVICE_TYPE_SPECIFICATION_NUMBER_OF_VERSION_LIMIT];
} BRFileServiceTypeSpecification;

extern BRFileService
fileServiceCreateFromTypeSpecfications (const char *basePath,
                                        const char *currency,
                                        const char *network,
                                        BRFileServiceContext context,
                                        BRFileServiceErrorHandler handler,
                                        size_t specificationsCount,
                                        BRFileServiceTypeSpecification *specfications);

///
/// Deletes file system data
///
/// @param basePath
/// @param currency
/// @param network
///
/// @return 0 on success, errno on failure
///
extern int
fileServiceWipe (const char *basePath,
                 const char *currency,
                 const char *network);

#endif /* BRFileService_h */

<MSG> Merge branch 'crypto' into ripple

<DFF> @@ -65,6 +65,7 @@ typedef struct {
 
         struct {
             int code;  // sqlite3_status_code
+            const char *reason;
         } sdb;
 
         struct {
@@ -90,9 +91,20 @@ fileServiceCreate (const char *basePath,
                    BRFileServiceContext context,
                    BRFileServiceErrorHandler handler);
 
+/**
+ * Release fs.  This will close `fs` if it hasn't been already and then free the memory and any
+ * other resources associaed with the fs (such as locks).
+ */
 extern void
 fileServiceRelease (BRFileService fs);
 
+/**
+ * Close fs.  This will close the DB associated with `fs`.  The `fs` must not be used after closing;
+ * if it is then an IMPL error is raised.  This can be called multiple times (but shouldn't be).
+ */
+extern void
+fileServiceClose (BRFileService fs);
+
 extern void
 fileServiceSetErrorHandler (BRFileService fs,
                             BRFileServiceContext context,
@@ -133,6 +145,11 @@ fileServiceClear (BRFileService fs,
 extern int
 fileServiceClearAll (BRFileService fs);
 
+extern UInt256
+fileServiceGetIdentifier (BRFileService fs,
+                          const char *type,
+                          const void *entity);
+
 /**
  * A function type to produce an identifer from an entity.  The identifer must be constant for
  * a particulary entity through time.  The identifier is used to derive a filename (more generally

{'commit': 'diff --git a/Java/Core/CMakeLists.txt b/Java/Core/CMakeLists.txt\nindex 2735a0b2..1e22e600 100644\n--- a/Java/Core/CMakeLists.txt\n+++ b/Java/Core/CMakeLists.txt\n@@ -290,6 +290,7 @@ target_sources (core\n                 src/main/cpp/core/ethereum/ewm/BREthereumEWM.h\n                 src/main/cpp/core/ethereum/ewm/BREthereumEWMClient.c\n                 src/main/cpp/core/ethereum/ewm/BREthereumEWMEvent.c\n+                src/main/cpp/core/ethereum/ewm/BREthereumEWMPersist.c\n                 src/main/cpp/core/ethereum/ewm/BREthereumEWMPrivate.h\n #                src/main/cpp/core/ethereum/ewm/testEwm.c\n                 )\ndiff --git a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/AmountAIT.java b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/AmountAIT.java\nindex 68357780..4ca009f1 100644\n--- a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/AmountAIT.java\n+++ b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/AmountAIT.java\n@@ -124,11 +124,24 @@ public class AmountAIT {\n         assertFalse(eth2.sub(eth2).get().isNegative());\n \n \n-        Amount a1 = Amount.create( "12.12345678", false, ether_eth).get();\n+        Amount a1 = Amount.create (+1.0, wei_eth);\n+        Amount a2 = Amount.create (-1.0, wei_eth);\n+        assertEquals(+0.0, a1.add(a2).get().doubleAmount(wei_eth).get(), 0.0);\n+        assertEquals(+0.0, a2.add(a1).get().doubleAmount(wei_eth).get(), 0.0);\n+        assertEquals(-2.0, a2.add(a2).get().doubleAmount(wei_eth).get(), 0.0);\n+        assertEquals(+2.0, a1.add(a1).get().doubleAmount(wei_eth).get(), 0.0);\n+\n+        assertEquals(+2.0, a1.sub(a2).get().doubleAmount(wei_eth).get(), 0.0);\n+        assertEquals(-2.0, a2.sub(a1).get().doubleAmount(wei_eth).get(), 0.0);\n+        assertEquals(+0.0, a1.sub(a1).get().doubleAmount(wei_eth).get(), 0.0);\n+        assertEquals(+0.0, a2.sub(a2).get().doubleAmount(wei_eth).get(), 0.0);\n+\n+\n+        a1 = Amount.create( "12.12345678", false, ether_eth).get();\n         assertTrue(a1.doubleAmount(ether_eth).isPresent());\n         assertEquals(new Double(12.12345678), a1.doubleAmount(ether_eth).get());\n \n-        Amount a2 = Amount.create( "123.12345678", false, ether_eth).get();\n+        a2 = Amount.create( "123.12345678", false, ether_eth).get();\n         assertTrue(a2.doubleAmount(ether_eth).isPresent());\n         assertEquals(new Double(123.12345678), a2.doubleAmount(ether_eth).get());\n \ndiff --git a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/EncrypterAIT.java b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/CipherAIT.java\nsimilarity index 79%\nrename from Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/EncrypterAIT.java\nrename to Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/CipherAIT.java\nindex 5a2cd2a7..47d0fc8f 100644\n--- a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/EncrypterAIT.java\n+++ b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/CipherAIT.java\n@@ -6,10 +6,10 @@ import java.nio.charset.StandardCharsets;\n \n import static org.junit.Assert.assertArrayEquals;\n \n-public class EncrypterAIT {\n+public class CipherAIT {\n \n     @Test\n-    public void testEncrypter() {\n+    public void testCipher() {\n         byte[] k;\n         byte[] d;\n         byte[] a;\n@@ -35,8 +35,8 @@ public class EncrypterAIT {\n                 (byte) 0x24, (byte) 0x45, (byte) 0xdf, (byte) 0x4f, (byte) 0x9b,\n                 (byte) 0x17, (byte) 0xad, (byte) 0x2b, (byte) 0x41, (byte) 0x7b,\n                 (byte) 0xe6, (byte) 0x6c, (byte) 0x37, (byte) 0x10};\n-        a = Encrypter.createForAesEcb(k).encrypt(d);\n-        assertArrayEquals(d, Encrypter.createForAesEcb(k).decrypt(a));\n+        a = Cipher.createForAesEcb(k).encrypt(d).get();\n+        assertArrayEquals(d, Cipher.createForAesEcb(k).decrypt(a).get());\n \n         // cha-cha\n \n@@ -49,8 +49,16 @@ public class EncrypterAIT {\n                 (byte) 0xc1, (byte) 0xc2, (byte) 0xc3, (byte) 0xc4, (byte) 0xc5,\n                 (byte) 0xc6, (byte) 0xc7};\n         byte[] msg = "Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.".getBytes(StandardCharsets.UTF_8);\n-        Key key = Key.createFromPrivateKeyString("5Kb8kLf9zgWQnogidDA76MzPL6TsZZY36hWXMssSzNydYXYB9KF".getBytes(StandardCharsets.UTF_8)).get();\n-        Encrypter alg = Encrypter.createForChaCha20Poly1305(key, nonce12, ad);\n+        Key key = Key.createFromSecret(new byte[] {\n+                (byte) 0x80, (byte) 0x81, (byte) 0x82, (byte) 0x83, (byte) 0x84,\n+                (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89,\n+                (byte) 0x8a, (byte) 0x8b, (byte) 0x8c, (byte) 0x8d, (byte) 0x8e,\n+                (byte) 0x8f, (byte) 0x90, (byte) 0x91, (byte) 0x92, (byte) 0x93,\n+                (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98,\n+                (byte) 0x99, (byte) 0x9a, (byte) 0x9b, (byte) 0x9c, (byte) 0x9d,\n+                (byte) 0x9e, (byte) 0x9f\n+        }).get();\n+        Cipher alg = Cipher.createForChaCha20Poly1305(key, nonce12, ad);\n \n         byte[] cipher = {\n                 (byte) 0xd3, (byte) 0x1a, (byte) 0x8d, (byte) 0x34, (byte) 0x64,\n@@ -80,14 +88,14 @@ public class EncrypterAIT {\n                 (byte) 0xe2, (byte) 0x6a, (byte) 0x7e, (byte) 0x90, (byte) 0x2e,\n                 (byte) 0xcb, (byte) 0xd0, (byte) 0x60, (byte) 0x06, (byte) 0x91\n         };\n-        assertArrayEquals(cipher, alg.encrypt(msg));\n-        assertArrayEquals(msg, alg.decrypt(cipher));\n+        assertArrayEquals(cipher, alg.encrypt(msg).get());\n+        assertArrayEquals(msg, alg.decrypt(cipher).get());\n \n         // pigeon\n \n         Key pubKey = Key.createFromPublicKeyString("02d404943960a71535a79679f1cf1df80e70597c05b05722839b38ebc8803af517".getBytes(StandardCharsets.UTF_8)).get();\n-        Encrypter pigeon = Encrypter.createForPigeon(key, pubKey, nonce12);\n-        byte [] pigeonCipher = pigeon.encrypt(msg);\n-        assertArrayEquals(msg, pigeon.decrypt(pigeonCipher));\n+        Cipher pigeon = Cipher.createForPigeon(key, pubKey, nonce12);\n+        byte [] pigeonCipher = pigeon.encrypt(msg).get();\n+        assertArrayEquals(msg, pigeon.decrypt(pigeonCipher).get());\n     }\n }\ndiff --git a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/CoderAIT.java b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/CoderAIT.java\nindex 0b4c7c49..e6c6244f 100644\n--- a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/CoderAIT.java\n+++ b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/CoderAIT.java\n@@ -19,7 +19,7 @@ public class CoderAIT {\n \n         d = new byte[] {(byte) 0xde, (byte)0xad, (byte)0xbe, (byte)0xef};\n         a = "deadbeef";\n-        r = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.HEX).encode(d);\n+        r = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.HEX).encode(d).get();\n         assertEquals(a, r);\n         assertArrayEquals(d, Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.HEX).decode(r).get());\n \n@@ -30,23 +30,23 @@ public class CoderAIT {\n \n         s = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";\n         d = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58).decode(s).get();\n-        assertEquals(s, Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58).encode(d));\n+        assertEquals(s, Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58).encode(d).get());\n \n         s = "z";\n         d = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58).decode(s).get();\n-        assertEquals(s, Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58).encode(d));\n+        assertEquals(s, Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58).encode(d).get());\n \n         //  BASE58CHECK\n \n         d = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};\n-        s = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58CHECK).encode(d);\n+        s = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58CHECK).encode(d).get();\n         assertArrayEquals(d, Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58CHECK).decode(s).get());\n \n         d = new byte[] {\n                 (byte) 0x05, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,\n                 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,\n                 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};\n-        s = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58CHECK).encode(d);\n+        s = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58CHECK).encode(d).get();\n         assertArrayEquals(d, Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.BASE58CHECK).decode(s).get());\n     }\n }\ndiff --git a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/HasherAIT.java b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/HasherAIT.java\nindex 75a59f88..ba980dd7 100644\n--- a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/HasherAIT.java\n+++ b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/HasherAIT.java\n@@ -22,7 +22,7 @@ public class HasherAIT {\n                 (byte) 0xc6, (byte) 0xd4, (byte) 0x8f, (byte) 0x60, (byte) 0x7d,\n                 (byte) 0x42, (byte) 0xc1, (byte) 0xd2, (byte) 0xa2, (byte) 0x15\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA1).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA1).hash(d).get());\n \n         // SHA256\n \n@@ -36,7 +36,7 @@ public class HasherAIT {\n                 (byte) 0x1c, (byte) 0x10, (byte) 0xad, (byte) 0x6e, (byte) 0xb0,\n                 (byte) 0x57, (byte) 0xd0\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256).hash(d).get());\n \n         // SHA256_2\n \n@@ -50,7 +50,7 @@ public class HasherAIT {\n                 (byte) 0x1e, (byte) 0xdf, (byte) 0xb9, (byte) 0xc1, (byte) 0x8d,\n                 (byte) 0x89, (byte) 0xb8\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256_2).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256_2).hash(d).get());\n \n         // SHA224\n \n@@ -63,7 +63,7 @@ public class HasherAIT {\n                 (byte) 0xc4, (byte) 0x92, (byte) 0xeb, (byte) 0x77, (byte) 0xd8,\n                 (byte) 0x22, (byte) 0x6a, (byte) 0x64\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA224).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA224).hash(d).get());\n \n         // SHA384\n \n@@ -80,7 +80,7 @@ public class HasherAIT {\n                 (byte) 0xd7, (byte) 0xfa, (byte) 0xcb, (byte) 0x84, (byte) 0xdf,\n                 (byte) 0x2b, (byte) 0xa2, (byte) 0x17\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA384).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA384).hash(d).get());\n \n         // SHA512\n \n@@ -100,7 +100,7 @@ public class HasherAIT {\n                 (byte) 0x82, (byte) 0x9e, (byte) 0xd0, (byte) 0xa2, (byte) 0xec,\n                 (byte) 0x89, (byte) 0x13, (byte) 0x21, (byte) 0xf3\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA512).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA512).hash(d).get());\n \n         // SHA3\n \n@@ -114,7 +114,7 @@ public class HasherAIT {\n                 (byte) 0xbf, (byte) 0xe2, (byte) 0x45, (byte) 0x11, (byte) 0x43,\n                 (byte) 0x15, (byte) 0x32\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA3).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA3).hash(d).get());\n \n         // RMD160\n \n@@ -125,7 +125,7 @@ public class HasherAIT {\n                 (byte) 0xf0, (byte) 0xcc, (byte) 0xc4, (byte) 0x91, (byte) 0xf0,\n                 (byte) 0xec, (byte) 0xbc, (byte) 0x7f, (byte) 0x94, (byte) 0x5b\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.RMD160).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.RMD160).hash(d).get());\n \n         // HASH160\n \n@@ -136,7 +136,7 @@ public class HasherAIT {\n                 (byte) 0x7f, (byte) 0x63, (byte) 0x4d, (byte) 0x34, (byte) 0xc5,\n                 (byte) 0xec, (byte) 0xd5, (byte) 0x23, (byte) 0x36, (byte) 0x72\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.HASH160).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.HASH160).hash(d).get());\n \n         // KECCAK256\n \n@@ -150,7 +150,7 @@ public class HasherAIT {\n                 (byte) 0xfa, (byte) 0xd8, (byte) 0x04, (byte) 0x5d, (byte) 0x85,\n                 (byte) 0xa4, (byte) 0x70\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.KECCAK256).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.KECCAK256).hash(d).get());\n \n         // MD5\n \n@@ -161,6 +161,6 @@ public class HasherAIT {\n                 (byte) 0x1a, (byte) 0x3b, (byte) 0xbd, (byte) 0xd3, (byte) 0x0c,\n                 (byte) 0xef\n         };\n-        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.MD5).hash(d));\n+        assertArrayEquals(a, Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.MD5).hash(d).get());\n     }\n }\ndiff --git a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/SignerAIT.java b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/SignerAIT.java\nindex 9fd11fc5..dd716bf9 100644\n--- a/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/SignerAIT.java\n+++ b/Java/CoreCrypto/src/androidTest/java/com/breadwallet/corecrypto/SignerAIT.java\n@@ -20,14 +20,14 @@ public class SignerAIT {\n         Signer signer;\n         byte[] answer;\n \n-        Key key = Key.createFromPrivateKeyString("5Kb8kLf9zgWQnogidDA76MzPL6TsZZY36hWXMssSzNydYXYB9KF".getBytes(StandardCharsets.UTF_8)).get();\n+        Key key = Key.createFromSecret(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}).get();\n \n         // Basic DER\n \n         msg = "How wonderful that we have met with a paradox. Now we have some hope of making progress.";\n-        digest = Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256).hash(msg.getBytes(StandardCharsets.UTF_8));\n+        digest = Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256).hash(msg.getBytes(StandardCharsets.UTF_8)).get();\n         signer = Signer.createForAlgorithm(com.breadwallet.crypto.Signer.Algorithm.BASIC_DER);\n-        signature = signer.sign(digest, key);\n+        signature = signer.sign(digest, key).get();\n \n         answer = new byte[] {\n                 (byte) 0x30, (byte) 0x45, (byte) 0x02, (byte) 0x21, (byte) 0x00,\n@@ -50,9 +50,9 @@ public class SignerAIT {\n         // Basic JOSE\n \n         msg = "How wonderful that we have met with a paradox. Now we have some hope of making progress.";\n-        digest = Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256).hash(msg.getBytes(StandardCharsets.UTF_8));\n+        digest = Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256).hash(msg.getBytes(StandardCharsets.UTF_8)).get();\n         signer = Signer.createForAlgorithm(com.breadwallet.crypto.Signer.Algorithm.BASIC_JOSE);\n-        signature = signer.sign(digest, key);\n+        signature = signer.sign(digest, key).get();\n \n         answer = new byte[] {\n                 (byte) 0xc0, (byte) 0xda, (byte) 0xfe, (byte) 0xc8, (byte) 0x25,\n@@ -73,9 +73,9 @@ public class SignerAIT {\n         // Compact\n \n         msg = "foo";\n-        digest = Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256).hash(msg.getBytes(StandardCharsets.UTF_8));\n+        digest = Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256).hash(msg.getBytes(StandardCharsets.UTF_8)).get();\n         signer = Signer.createForAlgorithm(com.breadwallet.crypto.Signer.Algorithm.COMPACT);\n-        signature = signer.sign(digest, key);\n+        signature = signer.sign(digest, key).get();\n \n         Optional<Key> keyPublic = signer.recover(digest, signature);\n         assertTrue(keyPublic.isPresent());\n@@ -99,15 +99,15 @@ public class SignerAIT {\n         };\n \n         byte[] message = Hasher.createForAlgorithm(com.breadwallet.crypto.Hasher.Algorithm.SHA256_2)\n-                .hash("Very deterministic message".getBytes(StandardCharsets.UTF_8));\n+                .hash("Very deterministic message".getBytes(StandardCharsets.UTF_8)).get();\n \n \n         for (int i = 0; i < secrets.length; i++) {\n             Optional<Key> maybeKey = Key.createFromPrivateKeyString(secrets[i]);\n             assertTrue(maybeKey.isPresent());\n \n-            byte[] outputSig = Signer.createForAlgorithm(com.breadwallet.crypto.Signer.Algorithm.COMPACT).sign(message, maybeKey.get());\n-            String outputSigHex = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.HEX).encode(outputSig);\n+            byte[] outputSig = Signer.createForAlgorithm(com.breadwallet.crypto.Signer.Algorithm.COMPACT).sign(message, maybeKey.get()).get();\n+            String outputSigHex = Coder.createForAlgorithm(com.breadwallet.crypto.Coder.Algorithm.HEX).encode(outputSig).get();\n             assertEquals(outputSigHex, signatures[i]);\n         }\n     }\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Cipher.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Cipher.java\nnew file mode 100644\nindex 00000000..49eaa21c\n--- /dev/null\n+++ b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Cipher.java\n@@ -0,0 +1,64 @@\n+/*\n+ * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n+ * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n+*\n+ * See the LICENSE file at the project root for license information.\n+ * See the CONTRIBUTORS file at the project root for a list of contributors.\n+ */\n+package com.breadwallet.corecrypto;\n+\n+import com.breadwallet.corenative.crypto.BRCryptoCipher;\n+import com.google.common.base.Optional;\n+\n+import static com.google.common.base.Preconditions.checkNotNull;\n+\n+/* package */\n+final class Cipher implements com.breadwallet.crypto.Cipher {\n+\n+    /* package */\n+    static Cipher createForAesEcb(byte[] key) {\n+        BRCryptoCipher cipher = BRCryptoCipher.createAesEcb(key).orNull();\n+        checkNotNull(cipher);\n+        return new Cipher(cipher);\n+    }\n+\n+    /* package */\n+    static Cipher createForChaCha20Poly1305(com.breadwallet.crypto.Key key, byte[] nonce12, byte[] ad) {\n+        BRCryptoCipher cipher = BRCryptoCipher.createChaCha20Poly1305(\n+                Key.from(key).getBRCryptoKey(),\n+                nonce12,\n+                ad)\n+                .orNull();\n+        checkNotNull(cipher);\n+        return new Cipher(cipher);\n+    }\n+\n+    /* package */\n+    static Cipher createForPigeon(com.breadwallet.crypto.Key privKey,\n+                                  com.breadwallet.crypto.Key pubKey,\n+                                  byte[] nonce12) {\n+        BRCryptoCipher cipher = BRCryptoCipher.createPigeon(\n+                Key.from(privKey).getBRCryptoKey(),\n+                Key.from(pubKey).getBRCryptoKey(),\n+                nonce12)\n+                .orNull();\n+        checkNotNull(cipher);\n+        return new Cipher(cipher);\n+    }\n+\n+    private final BRCryptoCipher core;\n+\n+    private Cipher(BRCryptoCipher core) {\n+        this.core = core;\n+    }\n+\n+    @Override\n+    public Optional<byte[]> encrypt(byte[] data) {\n+        return core.encrypt(data);\n+    }\n+\n+    @Override\n+    public Optional<byte[]> decrypt(byte[] data) {\n+        return core.decrypt(data);\n+    }\n+}\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Coder.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Coder.java\nindex fb23a45f..fd1c1ab9 100644\n--- a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Coder.java\n+++ b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Coder.java\n@@ -7,25 +7,46 @@\n  */\n package com.breadwallet.corecrypto;\n \n+import com.breadwallet.corenative.crypto.BRCryptoCoder;\n import com.google.common.base.Optional;\n \n+import static com.google.common.base.Preconditions.checkNotNull;\n+\n /* package */\n final class Coder implements com.breadwallet.crypto.Coder {\n \n-    // TODO(fix): Implement me!\n-\n     /* package */\n     static Coder createForAlgorithm(Algorithm algorithm) {\n-        return null;\n+        BRCryptoCoder core = null;\n+        switch (algorithm) {\n+            case HEX:\n+                core = BRCryptoCoder.createHex().orNull();\n+                break;\n+            case BASE58:\n+                core = BRCryptoCoder.createBase58().orNull();\n+                break;\n+            case BASE58CHECK:\n+                core = BRCryptoCoder.createBase58Check().orNull();\n+                break;\n+        }\n+\n+        checkNotNull(core);\n+        return new Coder(core);\n+    }\n+\n+    private final BRCryptoCoder core;\n+\n+    private Coder(BRCryptoCoder core) {\n+        this.core = core;\n     }\n \n     @Override\n-    public String encode(byte[] source) {\n-        return null;\n+    public Optional<String> encode(byte[] source) {\n+        return core.encode(source);\n     }\n \n     @Override\n     public Optional<byte[]> decode(String source) {\n-        return Optional.absent();\n+        return core.decode(source);\n     }\n }\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/CryptoApiProvider.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/CryptoApiProvider.java\nindex 5f9d8cb6..f073adce 100644\n--- a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/CryptoApiProvider.java\n+++ b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/CryptoApiProvider.java\n@@ -1,7 +1,7 @@\n /*\n  * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n  * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n-*\n+ *\n  * See the LICENSE file at the project root for license information.\n  * See the CONTRIBUTORS file at the project root for a list of contributors.\n  */\n@@ -13,6 +13,7 @@ import com.breadwallet.crypto.blockchaindb.BlockchainDb;\n import com.breadwallet.crypto.events.system.SystemListener;\n import com.google.common.base.Optional;\n \n+import java.io.File;\n import java.util.Date;\n import java.util.List;\n import java.util.concurrent.ScheduledExecutorService;\n@@ -74,6 +75,16 @@ public final class CryptoApiProvider implements CryptoApi.Provider {\n                                                     BlockchainDb query) {\n             return System.create(executor, listener, account, isMainnet, path, query);\n         }\n+\n+        @Override\n+        public void wipe(com.breadwallet.crypto.System system) {\n+            System.wipe(system);\n+        }\n+\n+        @Override\n+        public void wipeAll(String path, List<com.breadwallet.crypto.System> exemptSystems) {\n+            System.wipeAll(path, exemptSystems);\n+        }\n     };\n \n     private static final CryptoApi.CoderProvider coderProvider = new CryptoApi.CoderProvider() {\n@@ -83,21 +94,21 @@ public final class CryptoApiProvider implements CryptoApi.Provider {\n         }\n     };\n \n-    private static final CryptoApi.EncrypterProvider encrypterProvider = new CryptoApi.EncrypterProvider() {\n+    private static final CryptoApi.CipherProvider cipherProvider = new CryptoApi.CipherProvider() {\n         @Override\n-        public Encrypter createEncrypterrForAesEcb(byte[] key) {\n-            return Encrypter.createForAesEcb(key);\n+        public Cipher createCipherForAesEcb(byte[] key) {\n+            return Cipher.createForAesEcb(key);\n         }\n \n         @Override\n-        public Encrypter createEncrypterForChaCha20Poly1305(com.breadwallet.crypto.Key key, byte[] nonce12, byte[] ad) {\n-            return Encrypter.createForChaCha20Poly1305(key, nonce12, ad);\n+        public Cipher createCipherForChaCha20Poly1305(com.breadwallet.crypto.Key key, byte[] nonce12, byte[] ad) {\n+            return Cipher.createForChaCha20Poly1305(key, nonce12, ad);\n         }\n \n         @Override\n-        public Encrypter createEncrypterForPigeon(com.breadwallet.crypto.Key privKey,\n-                                                  com.breadwallet.crypto.Key pubKey, byte[] nonce12) {\n-            return Encrypter.createForPigeon(privKey, pubKey, nonce12);\n+        public Cipher createCipherForPigeon(com.breadwallet.crypto.Key privKey,\n+                                            com.breadwallet.crypto.Key pubKey, byte[] nonce12) {\n+            return Cipher.createForPigeon(privKey, pubKey, nonce12);\n         }\n     };\n \n@@ -191,8 +202,8 @@ public final class CryptoApiProvider implements CryptoApi.Provider {\n     }\n \n     @Override\n-    public CryptoApi.EncrypterProvider encrypterProvider() {\n-        return encrypterProvider;\n+    public CryptoApi.CipherProvider cipherProvider() {\n+        return cipherProvider;\n     }\n \n     @Override\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Encrypter.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Encrypter.java\ndeleted file mode 100644\nindex 7e823312..00000000\n--- a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Encrypter.java\n+++ /dev/null\n@@ -1,41 +0,0 @@\n-/*\n- * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n- * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n-*\n- * See the LICENSE file at the project root for license information.\n- * See the CONTRIBUTORS file at the project root for a list of contributors.\n- */\n-package com.breadwallet.corecrypto;\n-\n-/* package */\n-final class Encrypter implements com.breadwallet.crypto.Encrypter {\n-\n-    // TODO(fix): Implement me!\n-\n-    /* package */\n-    static Encrypter createForAesEcb(byte[] key) {\n-        return null;\n-    }\n-\n-    /* package */\n-    static Encrypter createForChaCha20Poly1305(com.breadwallet.crypto.Key key, byte[] nonce12, byte[] ad) {\n-        return null;\n-    }\n-\n-    /* package */\n-    static Encrypter createForPigeon(com.breadwallet.crypto.Key privKey,\n-                                     com.breadwallet.crypto.Key pubKey,\n-                                     byte[] nonce12) {\n-        return null;\n-    }\n-\n-    @Override\n-    public byte[] encrypt(byte[] data) {\n-        return new byte[0];\n-    }\n-\n-    @Override\n-    public byte[] decrypt(byte[] data) {\n-        return new byte[0];\n-    }\n-}\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Hasher.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Hasher.java\nindex 81b0c82e..fe668650 100644\n--- a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Hasher.java\n+++ b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Hasher.java\n@@ -7,18 +7,65 @@\n  */\n package com.breadwallet.corecrypto;\n \n+import com.breadwallet.corenative.crypto.BRCryptoHasher;\n+import com.google.common.base.Optional;\n+\n+import static com.google.common.base.Preconditions.checkNotNull;\n+\n /* package */\n final class Hasher implements com.breadwallet.crypto.Hasher {\n \n-    // TODO(fix): Implement me!\n-\n     /* package */\n     static Hasher createForAlgorithm(Algorithm algorithm) {\n-        return null;\n+        BRCryptoHasher core = null;\n+        switch (algorithm) {\n+            case SHA1:\n+                core = BRCryptoHasher.createSha1().orNull();\n+                break;\n+            case SHA224:\n+                core = BRCryptoHasher.createSha224().orNull();\n+                break;\n+            case SHA256:\n+                core = BRCryptoHasher.createSha256().orNull();\n+                break;\n+            case SHA256_2:\n+                core = BRCryptoHasher.createSha256_2().orNull();\n+                break;\n+            case SHA384:\n+                core = BRCryptoHasher.createSha384().orNull();\n+                break;\n+            case SHA512:\n+                core = BRCryptoHasher.createSha512().orNull();\n+                break;\n+            case SHA3:\n+                core = BRCryptoHasher.createSha3().orNull();\n+                break;\n+            case RMD160:\n+                core = BRCryptoHasher.createRmd160().orNull();\n+                break;\n+            case HASH160:\n+                core = BRCryptoHasher.createHash160().orNull();\n+                break;\n+            case KECCAK256:\n+                core = BRCryptoHasher.createKeccak256().orNull();\n+                break;\n+            case MD5:\n+                core = BRCryptoHasher.createMd5().orNull();\n+                break;\n+        }\n+\n+        checkNotNull(core);\n+        return new Hasher(core);\n+    }\n+\n+    private final BRCryptoHasher core;\n+\n+    private Hasher(BRCryptoHasher core) {\n+        this.core = core;\n     }\n \n     @Override\n-    public byte[] hash(byte[] data) {\n-        return new byte[0];\n+    public Optional<byte[]> hash(byte[] data) {\n+        return core.hash(data);\n     }\n }\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Key.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Key.java\nindex 3a68c10a..4e2f1bdc 100644\n--- a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Key.java\n+++ b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Key.java\n@@ -11,6 +11,7 @@ import android.support.annotation.Nullable;\n \n import com.breadwallet.corenative.crypto.BRCryptoKey;\n import com.google.common.base.Optional;\n+import com.google.common.base.Supplier;\n \n import java.util.List;\n \n@@ -94,6 +95,11 @@ final class Key implements com.breadwallet.crypto.Key {\n         return BRCryptoKey.createForBIP32BitID(phraseUtf8, index, uri, words).transform(Key::new);\n     }\n \n+    /* package */\n+    static Optional<Key> createFromSecret(byte[] secret) {\n+        return BRCryptoKey.cryptoKeyCreateFromSecret(secret).transform(Key::new);\n+    }\n+\n     /* package */\n     static Key create(BRCryptoKey core) {\n         return new Key(core);\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Signer.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Signer.java\nindex fb0e3cda..14dca659 100644\n--- a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Signer.java\n+++ b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/Signer.java\n@@ -7,25 +7,46 @@\n  */\n package com.breadwallet.corecrypto;\n \n+import com.breadwallet.corenative.crypto.BRCryptoSigner;\n import com.google.common.base.Optional;\n \n+import static com.google.common.base.Preconditions.checkNotNull;\n+\n /* package */\n final class Signer implements com.breadwallet.crypto.Signer {\n \n-    // TODO(fix): Implement me!\n-\n     /* package */\n     static Signer createForAlgorithm(Algorithm algorithm) {\n-        return null;\n+        BRCryptoSigner core = null;\n+        switch (algorithm) {\n+            case BASIC_DER:\n+                core = BRCryptoSigner.createBasicDer().orNull();\n+                break;\n+            case BASIC_JOSE:\n+                core = BRCryptoSigner.createBasicJose().orNull();\n+                break;\n+            case COMPACT:\n+                core = BRCryptoSigner.createCompact().orNull();\n+                break;\n+        }\n+\n+        checkNotNull(core);\n+        return new Signer(core);\n+    }\n+\n+    private final BRCryptoSigner core;\n+\n+    private Signer(BRCryptoSigner core) {\n+        this.core = core;\n     }\n \n     @Override\n-    public byte[] sign(byte[] data, com.breadwallet.crypto.Key key) {\n-        return new byte[0];\n+    public Optional<byte[]> sign(byte[] digest, com.breadwallet.crypto.Key key) {\n+        return core.sign(digest, Key.from(key).getBRCryptoKey());\n     }\n \n     @Override\n-    public Optional<Key> recover(byte[] data, byte[] signature) {\n-        return Optional.absent();\n+    public Optional<Key> recover(byte[] digest, byte[] signature) {\n+        return core.recover(digest, signature).transform(Key::create);\n     }\n }\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/System.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/System.java\nindex 497e6c4e..2775c37b 100644\n--- a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/System.java\n+++ b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/System.java\n@@ -91,7 +91,6 @@ import com.google.common.primitives.UnsignedLong;\n import com.sun.jna.Pointer;\n \n import java.io.File;\n-import java.lang.ref.WeakReference;\n import java.util.ArrayList;\n import java.util.Arrays;\n import java.util.Collections;\n@@ -116,9 +115,20 @@ final class System implements com.breadwallet.crypto.System {\n \n     private static final String TAG = System.class.getName();\n \n-    private static final Map<Pointer, WeakReference<System>> SYSTEMS = new ConcurrentHashMap<>();\n+    /// A index to globally identify systems.\n     private static final AtomicInteger SYSTEM_IDS = new AtomicInteger(0);\n \n+    /// A dictionary mapping an index to a system.\n+    private static final Map<Pointer, System> SYSTEMS_ACTIVE = new ConcurrentHashMap<>();\n+\n+    /// An array of removed systems.  This is a workaround for systems that have been destroyed.\n+    /// We do not correctly handle \'release\' and thus C-level memory issues are introduced; rather\n+    /// than solving those memory issues now, we\'ll avoid \'release\' by holding a reference.\n+    private static final List<System> SYSTEMS_INACTIVE = Collections.synchronizedList(new ArrayList<>());\n+\n+    /// If true, save removed system in the above array. Set to `false` for debugging \'release\'.\n+    private static final boolean SYSTEMS_INACTIVE_RETAIN = !BuildConfig.DEBUG;\n+\n     //\n     // Keep a static reference to the callbacks so that they are never GC\'ed\n     //\n@@ -155,24 +165,33 @@ final class System implements com.breadwallet.crypto.System {\n \n     private static final boolean DEFAULT_IS_NETWORK_REACHABLE = true;\n \n+    private static boolean ensurePath(String storagePath) {\n+        File storageFile = new File(storagePath);\n+        return ((storageFile.exists() || storageFile.mkdirs())\n+                && storageFile.isDirectory()\n+                && storageFile.canWrite());\n+    }\n+\n     /* package */\n     static System create(ScheduledExecutorService executor,\n                          SystemListener listener,\n                          com.breadwallet.crypto.Account account,\n                          boolean isMainnet,\n-                         String path,\n+                         String storagePath,\n                          BlockchainDb query) {\n-        Pointer context = Pointer.createConstant(SYSTEM_IDS.incrementAndGet());\n-\n         Account cryptoAccount = Account.from(account);\n-        path = path + (path.endsWith(File.separator) ? "" : File.separator) + cryptoAccount.getFilesystemIdentifier();\n \n-        BRCryptoCWMListener.ByValue cwmListener = new BRCryptoCWMListener.ByValue(context,\n+        storagePath = storagePath + (storagePath.endsWith(File.separator) ? "" : File.separator) + cryptoAccount.getFilesystemIdentifier();\n+        checkState(ensurePath(storagePath));\n+\n+        Pointer context = Pointer.createConstant(SYSTEM_IDS.incrementAndGet());\n+\n+        BRCryptoCWMListener cwmListener = new BRCryptoCWMListener(context,\n                 CWM_LISTENER_WALLET_MANAGER_CALLBACK,\n                 CWM_LISTENER_WALLET_CALLBACK,\n                 CWM_LISTENER_TRANSFER_CALLBACK);\n \n-        BRCryptoCWMClient.ByValue cwmClient = new BRCryptoCWMClient.ByValue(context,\n+        BRCryptoCWMClient cwmClient = new BRCryptoCWMClient(context,\n                 CWM_CLIENT_BTC,\n                 CWM_CLIENT_ETH,\n                 CWM_CLIENT_GEN);\n@@ -181,20 +200,91 @@ final class System implements com.breadwallet.crypto.System {\n                 listener,\n                 cryptoAccount,\n                 isMainnet,\n-                path,\n+                storagePath,\n                 query,\n+                context,\n                 cwmListener,\n                 cwmClient);\n \n-        SYSTEMS.put(context,\n-                new WeakReference<>(system));\n+        SYSTEMS_ACTIVE.put(context, system);\n \n         return system;\n     }\n \n+    /* package */\n+    static void wipe(com.breadwallet.crypto.System system) {\n+        // Safe the path to the persistent storage\n+        String storagePath = system.getPath();\n+\n+        // Destroy the system.\n+        destroy(system);\n+\n+        // Clear out persistent storage\n+        deleteRecursively(storagePath);\n+    }\n+\n+    /* package */\n+    static void wipeAll(String storagePath, List<com.breadwallet.crypto.System> exemptSystems) {\n+        Set<String> exemptSystemPath = new HashSet<>();\n+        for (com.breadwallet.crypto.System sys: exemptSystems) {\n+            exemptSystemPath.add(sys.getPath());\n+        }\n+\n+        File storageFile = new File(storagePath);\n+        for (File child : storageFile.listFiles()) {\n+            if (!exemptSystemPath.contains(child.getAbsolutePath())) {\n+                deleteRecursively(child);\n+            }\n+        }\n+    }\n+\n+    private static void destroy(com.breadwallet.crypto.System system) {\n+        System sys = System.from(system);\n+        // Stop all callbacks.  This might be inconsistent with \'deleted\' events.\n+        SYSTEMS_ACTIVE.remove(sys.context);\n+\n+        // Disconnect all wallet managers\n+        sys.disconnectAll();\n+\n+        // Stop\n+        sys.stopAll();\n+\n+        // Register the system as inactive\n+        if (SYSTEMS_INACTIVE_RETAIN) {\n+            SYSTEMS_INACTIVE.add(sys);\n+        }\n+    }\n+\n+    private static void deleteRecursively (String toDeletePath) {\n+        deleteRecursively(new File(toDeletePath));\n+    }\n+\n+    private static void deleteRecursively (File toDelete) {\n+        if (toDelete.isDirectory()) {\n+            for (File child : toDelete.listFiles()) {\n+                deleteRecursively(child);\n+            }\n+        }\n+\n+        if (toDelete.exists() && !toDelete.delete()) {\n+            Log.e(TAG, "Failed to delete " + toDelete.getAbsolutePath());\n+        }\n+    }\n+\n     private static Optional<System> getSystem(Pointer context) {\n-        WeakReference<System> ref = SYSTEMS.get(context);\n-        return null != ref ? Optional.fromNullable(ref.get()): Optional.absent();\n+        return Optional.fromNullable(SYSTEMS_ACTIVE.get(context));\n+    }\n+\n+    private static System from(com.breadwallet.crypto.System system) {\n+        if (system == null) {\n+            return null;\n+        }\n+\n+        if (system instanceof System) {\n+            return (System) system;\n+        }\n+\n+        throw new IllegalArgumentException("Unsupported system instance");\n     }\n \n     private final ExecutorService executor;\n@@ -202,10 +292,11 @@ final class System implements com.breadwallet.crypto.System {\n     private final SystemCallbackCoordinator callbackCoordinator;\n     private final Account account;\n     private final boolean isMainnet;\n-    private final String path;\n+    private final String storagePath;\n     private final BlockchainDb query;\n-    private final BRCryptoCWMListener.ByValue cwmListener;\n-    private final BRCryptoCWMClient.ByValue cwmClient;\n+    private final Pointer context;\n+    private final BRCryptoCWMListener cwmListener;\n+    private final BRCryptoCWMClient cwmClient;\n \n     private final Lock networksReadLock;\n     private final Lock networksWriteLock;\n@@ -220,17 +311,19 @@ final class System implements com.breadwallet.crypto.System {\n                    SystemListener listener,\n                    Account account,\n                    boolean isMainnet,\n-                   String path,\n+                   String storagePath,\n                    BlockchainDb query,\n-                   BRCryptoCWMListener.ByValue cwmListener,\n-                   BRCryptoCWMClient.ByValue cwmClient) {\n+                   Pointer context,\n+                   BRCryptoCWMListener cwmListener,\n+                   BRCryptoCWMClient cwmClient) {\n         this.executor = executor;\n         this.listener = listener;\n         this.callbackCoordinator = new SystemCallbackCoordinator(executor);\n         this.account = account;\n         this.isMainnet = isMainnet;\n-        this.path = path;\n+        this.storagePath = storagePath;\n         this.query = query;\n+        this.context = context;\n         this.cwmListener = cwmListener;\n         this.cwmClient = cwmClient;\n \n@@ -282,7 +375,7 @@ final class System implements com.breadwallet.crypto.System {\n                 Network.from(network),\n                 mode,\n                 scheme,\n-                path,\n+                storagePath,\n                 this,\n                 callbackCoordinator);\n \n@@ -299,7 +392,14 @@ final class System implements com.breadwallet.crypto.System {\n     }\n \n     @Override\n-    public void stop() {\n+    public void connectAll() {\n+        for (WalletManager manager: getWalletManagers()) {\n+            manager.connect(null);\n+        }\n+    }\n+\n+    @Override\n+    public void disconnectAll() {\n         for (WalletManager manager: getWalletManagers()) {\n             manager.disconnect();\n         }\n@@ -325,7 +425,7 @@ final class System implements com.breadwallet.crypto.System {\n \n     @Override\n     public String getPath() {\n-        return path;\n+        return storagePath;\n     }\n \n     @Override\n@@ -337,6 +437,12 @@ final class System implements com.breadwallet.crypto.System {\n         return wallets;\n     }\n \n+    private void stopAll() {\n+        for (WalletManager manager: getWalletManagers()) {\n+            manager.stop();\n+        }\n+    }\n+\n     // Network management\n \n     @Override\n@@ -481,7 +587,7 @@ final class System implements com.breadwallet.crypto.System {\n     //\n \n     private static void walletManagerEventCallback(Pointer context, @Nullable BRCryptoWalletManager coreWalletManager,\n-                                            BRCryptoWalletManagerEvent.ByValue event) {\n+                                            BRCryptoWalletManagerEvent event) {\n         Log.d(TAG, "WalletManagerEventCallback");\n \n         CoreBRCryptoWalletManager walletManager = CoreBRCryptoWalletManager.createOwned(coreWalletManager);\n@@ -778,7 +884,7 @@ final class System implements com.breadwallet.crypto.System {\n     //\n \n     private static void walletEventCallback(Pointer context, @Nullable BRCryptoWalletManager coreWalletManager,\n-                                     @Nullable BRCryptoWallet coreWallet, BRCryptoWalletEvent.ByValue event) {\n+                                     @Nullable BRCryptoWallet coreWallet, BRCryptoWalletEvent event) {\n         Log.d(TAG, "WalletEventCallback");\n \n         CoreBRCryptoWalletManager walletManager = CoreBRCryptoWalletManager.createOwned(coreWalletManager);\n@@ -1173,7 +1279,7 @@ final class System implements com.breadwallet.crypto.System {\n \n     private static void transferEventCallback(Pointer context, @Nullable BRCryptoWalletManager coreWalletManager,\n                                        @Nullable BRCryptoWallet coreWallet, @Nullable BRCryptoTransfer coreTransfer,\n-                                       BRCryptoTransferEvent.ByValue event) {\n+                                       BRCryptoTransferEvent event) {\n         Log.d(TAG, "TransferEventCallback");\n \n         CoreBRCryptoWalletManager walletManager = CoreBRCryptoWalletManager.createOwned(coreWalletManager);\ndiff --git a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/WalletManager.java b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/WalletManager.java\nindex c791b0b2..c43617fd 100644\n--- a/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/WalletManager.java\n+++ b/Java/CoreCrypto/src/main/java/com/breadwallet/corecrypto/WalletManager.java\n@@ -22,6 +22,7 @@ import com.breadwallet.crypto.errors.WalletSweeperError;\n import com.breadwallet.crypto.utility.CompletionHandler;\n import com.google.common.base.Optional;\n \n+import java.io.File;\n import java.util.ArrayList;\n import java.util.List;\n import java.util.Objects;\n@@ -33,13 +34,13 @@ import static com.google.common.base.Preconditions.checkState;\n final class WalletManager implements com.breadwallet.crypto.WalletManager {\n \n     /* package */\n-    static WalletManager create(BRCryptoCWMListener.ByValue listener,\n-                                BRCryptoCWMClient.ByValue client,\n+    static WalletManager create(BRCryptoCWMListener listener,\n+                                BRCryptoCWMClient client,\n                                 Account account,\n                                 Network network,\n                                 WalletManagerMode mode,\n                                 AddressScheme addressScheme,\n-                                String path,\n+                                String storagePath,\n                                 System system,\n                                 SystemCallbackCoordinator callbackCoordinator) {\n         return new WalletManager(\n@@ -50,7 +51,7 @@ final class WalletManager implements com.breadwallet.crypto.WalletManager {\n                         network.getCoreBRCryptoNetwork(),\n                         Utilities.walletManagerModeToCrypto(mode),\n                         Utilities.addressSchemeToCrypto(addressScheme),\n-                        path\n+                        storagePath\n                 ),\n                 system,\n                 callbackCoordinator\n@@ -113,6 +114,11 @@ final class WalletManager implements com.breadwallet.crypto.WalletManager {\n         core.sync();\n     }\n \n+    @Override\n+    public void stop() {\n+        core.stop();\n+    }\n+\n     @Override\n     public void syncToDepth(WalletManagerSyncDepth depth) {\n         core.syncToDepth(Utilities.syncDepthToCrypto(depth));\ndiff --git a/Java/CoreNative/CMakeLists.txt b/Java/CoreNative/CMakeLists.txt\nindex bf7fdbc8..a917a346 100644\n--- a/Java/CoreNative/CMakeLists.txt\n+++ b/Java/CoreNative/CMakeLists.txt\n@@ -252,6 +252,7 @@ target_sources (crypto\n                 src/main/cpp/core/ethereum/ewm/BREthereumEWM.h\n                 src/main/cpp/core/ethereum/ewm/BREthereumEWMClient.c\n                 src/main/cpp/core/ethereum/ewm/BREthereumEWMEvent.c\n+                src/main/cpp/core/ethereum/ewm/BREthereumEWMPersist.c\n                 src/main/cpp/core/ethereum/ewm/BREthereumEWMPrivate.h)\n \n # Ethereum Tests\n@@ -326,8 +327,16 @@ target_sources (crypto\n                 src/main/cpp/core/crypto/BRCryptoFeeBasis.h\n                 src/main/cpp/core/crypto/BRCryptoHash.c\n                 src/main/cpp/core/crypto/BRCryptoHash.h\n+                src/main/cpp/core/crypto/BRCryptoCipher.c\n+                src/main/cpp/core/crypto/BRCryptoCipher.h\n+                src/main/cpp/core/crypto/BRCryptoCoder.c\n+                src/main/cpp/core/crypto/BRCryptoCoder.h\n+                src/main/cpp/core/crypto/BRCryptoHasher.c\n+                src/main/cpp/core/crypto/BRCryptoHasher.h\n                 src/main/cpp/core/crypto/BRCryptoKey.c\n                 src/main/cpp/core/crypto/BRCryptoKey.h\n+                src/main/cpp/core/crypto/BRCryptoSigner.c\n+                src/main/cpp/core/crypto/BRCryptoSigner.h\n                 src/main/cpp/core/crypto/BRCryptoNetwork.c\n                 src/main/cpp/core/crypto/BRCryptoNetwork.h\n                 src/main/cpp/core/crypto/BRCryptoStatus.c\ndiff --git a/Java/CoreNative/src/androidTest/java/com/breadwallet/corenative/CryptoLibraryAIT.java b/Java/CoreNative/src/androidTest/java/com/breadwallet/corenative/CryptoLibraryAIT.java\nindex 0467849d..a63924de 100644\n--- a/Java/CoreNative/src/androidTest/java/com/breadwallet/corenative/CryptoLibraryAIT.java\n+++ b/Java/CoreNative/src/androidTest/java/com/breadwallet/corenative/CryptoLibraryAIT.java\n@@ -161,7 +161,7 @@ public class CryptoLibraryAIT {\n         boolean success;\n \n         BRCryptoAccount account = CoreBRCryptoAccount\n-                .createFromPhrase(paperKey.getBytes(StandardCharsets.UTF_8), epochDate)\n+                .createFromPhrase(paperKey.getBytes(StandardCharsets.UTF_8), UnsignedLong.valueOf(epoch))\n                 .asBRCryptoAccount();\n \n         //\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/CryptoLibrary.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/CryptoLibrary.java\nindex 0575140a..c459b011 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/CryptoLibrary.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/CryptoLibrary.java\n@@ -13,13 +13,17 @@ import com.breadwallet.corenative.crypto.BRCryptoAmount;\n import com.breadwallet.corenative.crypto.BRCryptoCWMClient;\n import com.breadwallet.corenative.crypto.BRCryptoCWMListener;\n import com.breadwallet.corenative.crypto.BRCryptoCWMClientCallbackState;\n+import com.breadwallet.corenative.crypto.BRCryptoCipher;\n+import com.breadwallet.corenative.crypto.BRCryptoCoder;\n import com.breadwallet.corenative.crypto.BRCryptoCurrency;\n import com.breadwallet.corenative.crypto.BRCryptoFeeBasis;\n import com.breadwallet.corenative.crypto.BRCryptoHash;\n+import com.breadwallet.corenative.crypto.BRCryptoHasher;\n import com.breadwallet.corenative.crypto.BRCryptoKey;\n import com.breadwallet.corenative.crypto.BRCryptoNetwork;\n import com.breadwallet.corenative.crypto.BRCryptoNetworkFee;\n import com.breadwallet.corenative.crypto.BRCryptoPeer;\n+import com.breadwallet.corenative.crypto.BRCryptoSigner;\n import com.breadwallet.corenative.crypto.BRCryptoTransfer;\n import com.breadwallet.corenative.crypto.BRCryptoTransferState;\n import com.breadwallet.corenative.crypto.BRCryptoUnit;\n@@ -114,6 +118,7 @@ public interface CryptoLibrary extends Library {\n     BRCryptoKey.OwnedBRCryptoKey cryptoKeyCreateForPigeon(BRCryptoKey key, byte[] nonce, SizeT nonceCount);\n     BRCryptoKey.OwnedBRCryptoKey cryptoKeyCreateForBIP32ApiAuth(ByteBuffer phraseBuffer, StringArray wordsArray);\n     BRCryptoKey.OwnedBRCryptoKey cryptoKeyCreateForBIP32BitID(ByteBuffer phraseBuffer, int index, String uri, StringArray wordsArray);\n+    BRCryptoKey.OwnedBRCryptoKey cryptoKeyCreateFromSecret(UInt256.ByValue secret);\n     void cryptoKeyProvidePublicKey(BRCryptoKey key, int useCompressed, int compressed);\n     int cryptoKeyHasSecret(BRCryptoKey key);\n     int cryptoKeyPublicMatch(BRCryptoKey key, BRCryptoKey other);\n@@ -247,6 +252,7 @@ public interface CryptoLibrary extends Library {\n     void cryptoWalletManagerDisconnect(BRCryptoWalletManager cwm);\n     void cryptoWalletManagerSync(BRCryptoWalletManager cwm);\n     void cryptoWalletManagerSyncToDepth(BRCryptoWalletManager cwm, int depth);\n+    void cryptoWalletManagerStop(BRCryptoWalletManager cwm);\n     void cryptoWalletManagerSubmit(BRCryptoWalletManager cwm, BRCryptoWallet wid, BRCryptoTransfer tid, ByteBuffer paperKey);\n     void cryptoWalletManagerSubmitForKey(BRCryptoWalletManager cwm, BRCryptoWallet wid, BRCryptoTransfer tid, BRCryptoKey key);\n     void cwmAnnounceGetBlockNumberSuccessAsInteger(BRCryptoWalletManager cwm, BRCryptoCWMClientCallbackState callbackState,long blockNumber);\n@@ -298,6 +304,36 @@ public interface CryptoLibrary extends Library {\n     int cryptoWalletSweeperValidate(BRCryptoWalletSweeper sweeper);\n     void cryptoWalletSweeperRelease(BRCryptoWalletSweeper sweeper);\n \n+    // crypto/BRCryptoCoder.h\n+    BRCryptoCoder.OwnedBRCryptoCoder cryptoCoderCreate(int type);\n+    SizeT cryptoCoderEncodeLength(BRCryptoCoder coder, byte[] src, SizeT srcLen);\n+    int cryptoCoderEncode(BRCryptoCoder coder, byte[] dst, SizeT dstLen, byte[] src, SizeT srcLen);\n+    SizeT cryptoCoderDecodeLength(BRCryptoCoder coder, byte[] src);\n+    int cryptoCoderDecode(BRCryptoCoder coder, byte[] dst, SizeT dstLen, byte[] src);\n+    void cryptoCoderGive(BRCryptoCoder coder);\n+\n+    // crypto/BRCryptoCipher.h\n+    BRCryptoCipher.OwnedBRCryptoCipher cryptoCipherCreateForAESECB(byte[] key, SizeT keyLen);\n+    BRCryptoCipher.OwnedBRCryptoCipher cryptoCipherCreateForChacha20Poly1305(BRCryptoKey key, byte[] nonce12, SizeT nonce12Len, byte[] ad, SizeT adLen);\n+    BRCryptoCipher.OwnedBRCryptoCipher cryptoCipherCreateForPigeon(BRCryptoKey privKey, BRCryptoKey pubKey, byte[] nonce12, SizeT nonce12Len);\n+    SizeT cryptoCipherEncryptLength(BRCryptoCipher cipher, byte[] src, SizeT srcLen);\n+    int cryptoCipherEncrypt(BRCryptoCipher cipher, byte[] dst, SizeT dstLen, byte[] src, SizeT srcLen);\n+    SizeT cryptoCipherDecryptLength(BRCryptoCipher cipher, byte[] src, SizeT srcLen);\n+    int cryptoCipherDecrypt(BRCryptoCipher cipher, byte[] dst, SizeT dstLen, byte[] src, SizeT srcLen);\n+    void cryptoCipherGive(BRCryptoCipher cipher);\n+\n+    // crypto/BRCryptoHasher.h\n+    BRCryptoHasher.OwnedBRCryptoHasher cryptoHasherCreate(int type);\n+    SizeT cryptoHasherLength(BRCryptoHasher hasher);\n+    int cryptoHasherHash(BRCryptoHasher hasher, byte[] dst, SizeT dstLen, byte[] src, SizeT srcLen);\n+    void cryptoHasherGive(BRCryptoHasher hasher);\n+\n+    // crypto/BRCryptoSigner.h\n+    BRCryptoSigner.OwnedBRCryptoSigner cryptoSignerCreate(int type);\n+    SizeT cryptoSignerSignLength(BRCryptoSigner signer, BRCryptoKey key, byte[] digest, SizeT digestlen);\n+    int cryptoSignerSign(BRCryptoSigner signer, BRCryptoKey key, byte[] signature, SizeT signatureLen, byte[] digest, SizeT digestLen);\n+    BRCryptoKey.OwnedBRCryptoKey cryptoSignerRecover(BRCryptoSigner signer, byte[] digest, SizeT digestLen, byte[] signature, SizeT signatureLen);\n+    void cryptoSignerGive(BRCryptoSigner signer);\n \n     // ethereum/util/BRUtilMath.h\n     Pointer coerceStringPrefaced(UInt256.ByValue value, int base, String preface);\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClient.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClient.java\nindex 8324bb9d..06d8b12c 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClient.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClient.java\n@@ -40,13 +40,20 @@ public class BRCryptoCWMClient extends Structure {\n         super(peer);\n     }\n \n+    public ByValue toByValue() {\n+        ByValue other = new ByValue();\n+        other.context = this.context;\n+        other.btc = this.btc.toByValue();\n+        other.eth = this.eth.toByValue();\n+        other.gen = this.gen.toByValue();\n+        return other;\n+    }\n+\n     public static class ByReference extends BRCryptoCWMClient implements Structure.ByReference {\n \n     }\n \n     public static class ByValue extends BRCryptoCWMClient implements Structure.ByValue {\n-        public ByValue(Pointer context, BRCryptoCWMClientBtc btc, BRCryptoCWMClientEth eth, BRCryptoCWMClientGen gen) {\n-            super(context, btc, eth, gen);\n-        }\n+\n     }\n }\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientBtc.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientBtc.java\nindex af909347..57a80490 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientBtc.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientBtc.java\n@@ -57,6 +57,14 @@ public class BRCryptoCWMClientBtc extends Structure {\n         super(peer);\n     }\n \n+    public ByValue toByValue() {\n+        ByValue other = new ByValue();\n+        other.funcGetBlockNumber = this.funcGetBlockNumber;\n+        other.funcGetTransactions = this.funcGetTransactions;\n+        other.funcSubmitTransaction = this.funcSubmitTransaction;\n+        return other;\n+    }\n+\n     public static class ByReference extends BRCryptoCWMClientBtc implements Structure.ByReference {\n \n     }\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientEth.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientEth.java\nindex 5474bc38..0b9cd662 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientEth.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientEth.java\n@@ -119,6 +119,22 @@ public class BRCryptoCWMClientEth extends Structure {\n         super(peer);\n     }\n \n+    public BRCryptoCWMClientEth toByValue() {\n+        ByValue other = new ByValue();\n+        other.funcGetEtherBalance = this.funcGetEtherBalance;\n+        other.funcGetTokenBalance = this.funcGetTokenBalance;\n+        other.funcGetGasPrice = this.funcGetGasPrice;\n+        other.funcEstimateGas = this.funcEstimateGas;\n+        other.funcSubmitTransaction = this.funcSubmitTransaction;\n+        other.funcGetTransactions = this.funcGetTransactions;\n+        other.funcGetLogs = this.funcGetLogs;\n+        other.funcGetBlocks = this.funcGetBlocks;\n+        other.funcGetTokens = this.funcGetTokens;\n+        other.funcGetBlockNumber = this.funcGetBlockNumber;\n+        other.funcGetNonce = this.funcGetNonce;\n+        return other;\n+    }\n+\n     public static class ByReference extends BRCryptoCWMClientEth implements Structure.ByReference {\n \n     }\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientGen.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientGen.java\nindex 3cbbd3ec..a4808497 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientGen.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMClientGen.java\n@@ -53,6 +53,14 @@ public class BRCryptoCWMClientGen extends Structure {\n         this.funcSubmitTransaction = funcSubmitTransaction;\n     }\n \n+    public ByValue toByValue() {\n+        ByValue other = new ByValue();\n+        other.funcGetBlockNumber = this.funcGetBlockNumber;\n+        other.funcGetTransactions = this.funcGetTransactions;\n+        other.funcSubmitTransaction = this.funcSubmitTransaction;\n+        return other;\n+    }\n+\n     public BRCryptoCWMClientGen(Pointer peer) {\n         super(peer);\n     }\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMListener.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMListener.java\nindex 5a1a9af2..d4c2f334 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMListener.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCWMListener.java\n@@ -50,13 +50,20 @@ public class BRCryptoCWMListener extends Structure {\n         super(peer);\n     }\n \n+    public ByValue toByValue() {\n+        ByValue other = new ByValue();\n+        other.context = this.context;\n+        other.walletManagerEventCallback = this.walletManagerEventCallback;\n+        other.walletEventCallback = this.walletEventCallback;\n+        other.transferEventCallback = this.transferEventCallback;\n+        return other;\n+    }\n+\n     public static class ByReference extends BRCryptoCWMListener implements Structure.ByReference {\n \n     }\n \n     public static class ByValue extends BRCryptoCWMListener implements Structure.ByValue {\n-        public ByValue(Pointer context, BRCryptoCWMListenerWalletManagerEvent walletManagerEventCallback, BRCryptoCWMListenerWalletEvent walletEventCallback, BRCryptoCWMListenerTransferEvent transferEventCallback) {\n-            super(context, walletManagerEventCallback, walletEventCallback, transferEventCallback);\n-        }\n+\n     }\n }\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCipher.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCipher.java\nnew file mode 100644\nindex 00000000..78b878cf\n--- /dev/null\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCipher.java\n@@ -0,0 +1,93 @@\n+/*\n+ * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n+ * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n+*\n+ * See the LICENSE file at the project root for license information.\n+ * See the CONTRIBUTORS file at the project root for a list of contributors.\n+ */\n+package com.breadwallet.corenative.crypto;\n+\n+import com.breadwallet.corenative.CryptoLibrary;\n+import com.breadwallet.corenative.utility.SizeT;\n+import com.google.common.base.Optional;\n+import com.google.common.primitives.Ints;\n+import com.sun.jna.Pointer;\n+import com.sun.jna.PointerType;\n+\n+import static com.google.common.base.Preconditions.checkState;\n+\n+public class BRCryptoCipher extends PointerType {\n+\n+    public static Optional<BRCryptoCipher> createAesEcb(byte[] key) {\n+        return Optional.fromNullable(\n+                CryptoLibrary.INSTANCE.cryptoCipherCreateForAESECB(key, new SizeT(key.length))\n+        );\n+    }\n+\n+    public static Optional<BRCryptoCipher> createChaCha20Poly1305(BRCryptoKey key, byte[] nonce12, byte[] ad) {\n+        return Optional.fromNullable(\n+                CryptoLibrary.INSTANCE.cryptoCipherCreateForChacha20Poly1305(\n+                        key,\n+                        nonce12,\n+                        new SizeT(nonce12.length),\n+                        ad,\n+                        new SizeT(ad.length))\n+        );\n+    }\n+\n+    public static Optional<BRCryptoCipher> createPigeon(BRCryptoKey privKey, BRCryptoKey pubKey, byte[] nonce12) {\n+        return Optional.fromNullable(\n+                CryptoLibrary.INSTANCE.cryptoCipherCreateForPigeon(\n+                        privKey,\n+                        pubKey,\n+                        nonce12,\n+                        new SizeT(nonce12.length))\n+        );\n+    }\n+\n+    public BRCryptoCipher(Pointer address) {\n+        super(address);\n+    }\n+\n+    public BRCryptoCipher() {\n+        super();\n+    }\n+\n+    public Optional<byte[]> encrypt(byte[] input) {\n+        SizeT length = CryptoLibrary.INSTANCE.cryptoCipherEncryptLength(this, input, new SizeT(input.length));\n+        int lengthAsInt = Ints.checkedCast(length.longValue());\n+        if (0 == lengthAsInt) return Optional.absent();\n+\n+        byte[] output = new byte[lengthAsInt];\n+        int result = CryptoLibrary.INSTANCE.cryptoCipherEncrypt(this, output, new SizeT(output.length), input, new SizeT(input.length));\n+        return result == BRCryptoBoolean.CRYPTO_TRUE ? Optional.of(output) : Optional.absent();\n+    }\n+\n+    public Optional<byte[]> decrypt(byte[] input) {\n+        SizeT length = CryptoLibrary.INSTANCE.cryptoCipherDecryptLength(this, input, new SizeT(input.length));\n+        int lengthAsInt = Ints.checkedCast(length.longValue());\n+        if (0 == lengthAsInt) return Optional.absent();\n+\n+        byte[] output = new byte[lengthAsInt];\n+        int result = CryptoLibrary.INSTANCE.cryptoCipherDecrypt(this, output, new SizeT(output.length), input, new SizeT(input.length));\n+        return result == BRCryptoBoolean.CRYPTO_TRUE ? Optional.of(output) : Optional.absent();\n+    }\n+\n+    public static class OwnedBRCryptoCipher extends BRCryptoCipher {\n+\n+        public OwnedBRCryptoCipher(Pointer address) {\n+            super(address);\n+        }\n+\n+        public OwnedBRCryptoCipher() {\n+            super();\n+        }\n+\n+        @Override\n+        protected void finalize() {\n+            if (null != getPointer()) {\n+                CryptoLibrary.INSTANCE.cryptoCipherGive(this);\n+            }\n+        }\n+    }\n+}\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCoder.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCoder.java\nnew file mode 100644\nindex 00000000..c56d4bb4\n--- /dev/null\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoCoder.java\n@@ -0,0 +1,103 @@\n+/*\n+ * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n+ * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n+*\n+ * See the LICENSE file at the project root for license information.\n+ * See the CONTRIBUTORS file at the project root for a list of contributors.\n+ */\n+package com.breadwallet.corenative.crypto;\n+\n+import com.breadwallet.corenative.CryptoLibrary;\n+import com.breadwallet.corenative.utility.SizeT;\n+import com.google.common.base.Optional;\n+import com.google.common.primitives.Ints;\n+import com.sun.jna.Pointer;\n+import com.sun.jna.PointerType;\n+\n+import java.nio.charset.StandardCharsets;\n+import java.util.Arrays;\n+\n+import static com.google.common.base.Preconditions.checkState;\n+\n+public class BRCryptoCoder extends PointerType {\n+\n+    // these must mirror BRCryptoCoderType\'s enum values\n+    private static final int CRYPTO_CODER_HEX         = 0;\n+    private static final int CRYPTO_CODER_BASE58      = 1;\n+    private static final int CRYPTO_CODER_BASE58CHECK = 2;\n+\n+    public static Optional<BRCryptoCoder> createHex() {\n+        return create(CRYPTO_CODER_HEX);\n+    }\n+\n+    public static Optional<BRCryptoCoder> createBase58() {\n+        return create(CRYPTO_CODER_BASE58);\n+    }\n+\n+    public static Optional<BRCryptoCoder> createBase58Check() {\n+        return create(CRYPTO_CODER_BASE58CHECK);\n+    }\n+\n+    private static Optional<BRCryptoCoder> create(int alg) {\n+        return Optional.fromNullable(CryptoLibrary.INSTANCE.cryptoCoderCreate(alg));\n+    }\n+\n+    public BRCryptoCoder(Pointer address) {\n+        super(address);\n+    }\n+\n+    public BRCryptoCoder() {\n+        super();\n+    }\n+\n+    public Optional<String> encode(byte[] input) {\n+        SizeT length = CryptoLibrary.INSTANCE.cryptoCoderEncodeLength(this, input, new SizeT(input.length));\n+        int lengthAsInt = Ints.checkedCast(length.longValue());\n+        if (0 == lengthAsInt) return Optional.absent();\n+\n+        byte[] output = new byte[lengthAsInt];\n+        int result = CryptoLibrary.INSTANCE.cryptoCoderEncode(this, output, new SizeT(output.length), input, new SizeT(input.length));\n+        return result == BRCryptoBoolean.CRYPTO_TRUE ? Optional.of(utf8BytesToString(output)) : Optional.absent();\n+    }\n+\n+    public Optional<byte[]> decode(String inputStr) {\n+        // ensure string is null terminated\n+        byte[] inputWithoutTerminator = inputStr.getBytes(StandardCharsets.UTF_8);\n+        byte[] inputWithTerminator = Arrays.copyOf(inputWithoutTerminator, inputWithoutTerminator.length + 1);\n+\n+        SizeT length = CryptoLibrary.INSTANCE.cryptoCoderDecodeLength(this, inputWithTerminator);\n+        int lengthAsInt = Ints.checkedCast(length.longValue());\n+        if (0 == lengthAsInt) return Optional.absent();\n+\n+        byte[] output = new byte[lengthAsInt];\n+        int result = CryptoLibrary.INSTANCE.cryptoCoderDecode(this, output, new SizeT(output.length), inputWithTerminator);\n+        return result == BRCryptoBoolean.CRYPTO_TRUE ? Optional.of(output) : Optional.absent();\n+    }\n+\n+    private static String utf8BytesToString(byte[] message) {\n+        int end = 0;\n+        int len = message.length;\n+        while ((end < len) && (message[end] != 0)) {\n+            end++;\n+        }\n+        return new String(message, 0, end, StandardCharsets.UTF_8);\n+    }\n+\n+    public static class OwnedBRCryptoCoder extends BRCryptoCoder {\n+\n+        public OwnedBRCryptoCoder(Pointer address) {\n+            super(address);\n+        }\n+\n+        public OwnedBRCryptoCoder() {\n+            super();\n+        }\n+\n+        @Override\n+        protected void finalize() {\n+            if (null != getPointer()) {\n+                CryptoLibrary.INSTANCE.cryptoCoderGive(this);\n+            }\n+        }\n+    }\n+}\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoHasher.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoHasher.java\nnew file mode 100644\nindex 00000000..d1ba91e4\n--- /dev/null\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoHasher.java\n@@ -0,0 +1,127 @@\n+/*\n+ * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n+ * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n+*\n+ * See the LICENSE file at the project root for license information.\n+ * See the CONTRIBUTORS file at the project root for a list of contributors.\n+ */\n+package com.breadwallet.corenative.crypto;\n+\n+import com.breadwallet.corenative.CryptoLibrary;\n+import com.breadwallet.corenative.utility.SizeT;\n+import com.google.common.base.Optional;\n+import com.google.common.primitives.Ints;\n+import com.google.common.primitives.UnsignedInteger;\n+import com.google.common.primitives.UnsignedInts;\n+import com.google.common.primitives.UnsignedLong;\n+import com.sun.jna.Memory;\n+import com.sun.jna.Native;\n+import com.sun.jna.Pointer;\n+import com.sun.jna.PointerType;\n+import com.sun.jna.StringArray;\n+\n+import java.nio.ByteBuffer;\n+import java.util.Arrays;\n+import java.util.List;\n+\n+import static com.google.common.base.Preconditions.checkState;\n+\n+public class BRCryptoHasher extends PointerType {\n+\n+    // these must mirror BRCryptoHasherType\'s enum values\n+    private static final int CRYPTO_HASHER_SHA1         = 0;\n+    private static final int CRYPTO_HASHER_SHA224       = 1;\n+    private static final int CRYPTO_HASHER_SHA256       = 2;\n+    private static final int CRYPTO_HASHER_SHA256_2     = 3;\n+    private static final int CRYPTO_HASHER_SHA384       = 4;\n+    private static final int CRYPTO_HASHER_SHA512       = 5;\n+    private static final int CRYPTO_HASHER_SHA3         = 6;\n+    private static final int CRYPTO_HASHER_RMD160       = 7;\n+    private static final int CRYPTO_HASHER_HASH160      = 8;\n+    private static final int CRYPTO_HASHER_KECCAK256    = 9;\n+    private static final int CRYPTO_HASHER_MD5          = 10;\n+\n+    public static Optional<BRCryptoHasher> createSha1() {\n+        return create(CRYPTO_HASHER_SHA1);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createSha224() {\n+        return create(CRYPTO_HASHER_SHA224);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createSha256() {\n+        return create(CRYPTO_HASHER_SHA256);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createSha256_2() {\n+        return create(CRYPTO_HASHER_SHA256_2);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createSha384() {\n+        return create(CRYPTO_HASHER_SHA384);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createSha512() {\n+        return create(CRYPTO_HASHER_SHA512);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createSha3() {\n+        return create(CRYPTO_HASHER_SHA3);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createRmd160() {\n+        return create(CRYPTO_HASHER_RMD160);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createHash160() {\n+        return create(CRYPTO_HASHER_HASH160);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createKeccak256() {\n+        return create(CRYPTO_HASHER_KECCAK256);\n+    }\n+\n+    public static Optional<BRCryptoHasher> createMd5() {\n+        return create(CRYPTO_HASHER_MD5);\n+    }\n+\n+    private static Optional<BRCryptoHasher> create(int alg) {\n+        return Optional.fromNullable(CryptoLibrary.INSTANCE.cryptoHasherCreate(alg));\n+    }\n+\n+    public BRCryptoHasher(Pointer address) {\n+        super(address);\n+    }\n+\n+    public BRCryptoHasher() {\n+        super();\n+    }\n+\n+    public Optional<byte[]> hash(byte[] data) {\n+        SizeT length = CryptoLibrary.INSTANCE.cryptoHasherLength(this);\n+        int lengthAsInt = Ints.checkedCast(length.longValue());\n+        if (0 == lengthAsInt) return Optional.absent();\n+\n+        byte[] hash = new byte[lengthAsInt];\n+        int result = CryptoLibrary.INSTANCE.cryptoHasherHash(this, hash, new SizeT(hash.length), data, new SizeT(data.length));\n+        return result == BRCryptoBoolean.CRYPTO_TRUE ? Optional.of(hash) : Optional.absent();\n+    }\n+\n+    public static class OwnedBRCryptoHasher extends BRCryptoHasher {\n+\n+        public OwnedBRCryptoHasher(Pointer address) {\n+            super(address);\n+        }\n+\n+        public OwnedBRCryptoHasher() {\n+            super();\n+        }\n+\n+        @Override\n+        protected void finalize() {\n+            if (null != getPointer()) {\n+                CryptoLibrary.INSTANCE.cryptoHasherGive(this);\n+            }\n+        }\n+    }\n+}\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoKey.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoKey.java\nindex 372bc51b..f3f2d9b4 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoKey.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoKey.java\n@@ -8,6 +8,7 @@\n package com.breadwallet.corenative.crypto;\n \n import com.breadwallet.corenative.CryptoLibrary;\n+import com.breadwallet.corenative.support.UInt256;\n import com.breadwallet.corenative.utility.SizeT;\n import com.google.common.base.Optional;\n import com.sun.jna.FromNativeContext;\n@@ -181,6 +182,10 @@ public class BRCryptoKey extends PointerType {\n         }\n     }\n \n+    public static Optional<BRCryptoKey> cryptoKeyCreateFromSecret(byte[] secret) {\n+        return Optional.fromNullable(CryptoLibrary.INSTANCE.cryptoKeyCreateFromSecret(new UInt256(secret).toByValue()));\n+    }\n+\n     public byte[] encodeAsPrivate() {\n         Pointer ptr = CryptoLibrary.INSTANCE.cryptoKeyEncodePrivate(this);\n         try {\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoSigner.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoSigner.java\nnew file mode 100644\nindex 00000000..1afe2870\n--- /dev/null\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoSigner.java\n@@ -0,0 +1,92 @@\n+/*\n+ * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n+ * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n+*\n+ * See the LICENSE file at the project root for license information.\n+ * See the CONTRIBUTORS file at the project root for a list of contributors.\n+ */\n+package com.breadwallet.corenative.crypto;\n+\n+import com.breadwallet.corenative.CryptoLibrary;\n+import com.breadwallet.corenative.utility.SizeT;\n+import com.google.common.base.Optional;\n+import com.google.common.primitives.Ints;\n+import com.sun.jna.Pointer;\n+import com.sun.jna.PointerType;\n+\n+import static com.google.common.base.Preconditions.checkState;\n+\n+public class BRCryptoSigner extends PointerType {\n+\n+    // these must mirror BRCryptoCoderType\'s enum values\n+    private static final int CRYPTO_SIGNER_BASIC_DER  = 0;\n+    private static final int CRYPTO_SIGNER_BASIC_JOSE = 1;\n+    private static final int CRYPTO_SIGNER_COMPACT    = 2;\n+\n+    public static Optional<BRCryptoSigner> createBasicDer() {\n+        return create(CRYPTO_SIGNER_BASIC_DER);\n+    }\n+\n+    public static Optional<BRCryptoSigner> createBasicJose() {\n+        return create(CRYPTO_SIGNER_BASIC_JOSE);\n+    }\n+\n+    public static Optional<BRCryptoSigner> createCompact() {\n+        return create(CRYPTO_SIGNER_COMPACT);\n+    }\n+\n+    private static Optional<BRCryptoSigner> create(int alg) {\n+        return Optional.fromNullable(CryptoLibrary.INSTANCE.cryptoSignerCreate(alg));\n+    }\n+\n+    public BRCryptoSigner(Pointer address) {\n+        super(address);\n+    }\n+\n+    public BRCryptoSigner() {\n+        super();\n+    }\n+\n+    public Optional<byte[]> sign(byte[] digest, BRCryptoKey key) {\n+        checkState(32 == digest.length);\n+\n+        SizeT length = CryptoLibrary.INSTANCE.cryptoSignerSignLength(this, key, digest, new SizeT(digest.length));\n+        int lengthAsInt = Ints.checkedCast(length.longValue());\n+        if (0 == lengthAsInt) return Optional.absent();\n+\n+        byte[] signature = new byte[lengthAsInt];\n+        int result = CryptoLibrary.INSTANCE.cryptoSignerSign(this, key, signature, new SizeT(signature.length), digest, new SizeT(digest.length));\n+        return result == BRCryptoBoolean.CRYPTO_TRUE ? Optional.of(signature) : Optional.absent();\n+    }\n+\n+    public Optional<BRCryptoKey > recover(byte[] digest, byte[] signature) {\n+        checkState(32 == digest.length);\n+        return Optional.fromNullable(\n+                CryptoLibrary.INSTANCE.cryptoSignerRecover(\n+                        this,\n+                        digest,\n+                        new SizeT(digest.length),\n+                        signature,\n+                        new SizeT(signature.length)\n+                )\n+        );\n+    }\n+\n+    public static class OwnedBRCryptoSigner extends BRCryptoSigner {\n+\n+        public OwnedBRCryptoSigner(Pointer address) {\n+            super(address);\n+        }\n+\n+        public OwnedBRCryptoSigner() {\n+            super();\n+        }\n+\n+        @Override\n+        protected void finalize() {\n+            if (null != getPointer()) {\n+                CryptoLibrary.INSTANCE.cryptoSignerGive(this);\n+            }\n+        }\n+    }\n+}\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoWalletManager.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoWalletManager.java\nindex 73ee4388..63fd057c 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoWalletManager.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/BRCryptoWalletManager.java\n@@ -138,6 +138,11 @@ public class BRCryptoWalletManager extends PointerType implements CoreBRCryptoWa\n         CryptoLibrary.INSTANCE.cryptoWalletManagerSync(this);\n     }\n \n+    @Override\n+    public void stop() {\n+        CryptoLibrary.INSTANCE.cryptoWalletManagerStop(this);\n+    }\n+\n     @Override\n     public void syncToDepth(BRSyncDepth depth) {\n         CryptoLibrary.INSTANCE.cryptoWalletManagerSyncToDepth(this, depth.toNative());\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/CoreBRCryptoWalletManager.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/CoreBRCryptoWalletManager.java\nindex 51df56e1..671c7c60 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/CoreBRCryptoWalletManager.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/CoreBRCryptoWalletManager.java\n@@ -17,11 +17,24 @@ import java.util.List;\n \n public interface CoreBRCryptoWalletManager {\n \n-    static CoreBRCryptoWalletManager create(BRCryptoCWMListener.ByValue listener, BRCryptoCWMClient.ByValue client,\n-                                            CoreBRCryptoAccount account, CoreBRCryptoNetwork network, int mode,\n-                                            int scheme, String path) {\n-        return new OwnedBRCryptoWalletManager(CryptoLibrary.INSTANCE.cryptoWalletManagerCreate(\n-                listener, client, account.asBRCryptoAccount(), network.asBRCryptoNetwork(), mode, scheme, path));\n+    static CoreBRCryptoWalletManager create(BRCryptoCWMListener listener,\n+                                            BRCryptoCWMClient client,\n+                                            CoreBRCryptoAccount account,\n+                                            CoreBRCryptoNetwork network,\n+                                            int mode,\n+                                            int scheme,\n+                                            String path) {\n+        return new OwnedBRCryptoWalletManager(\n+                CryptoLibrary.INSTANCE.cryptoWalletManagerCreate(\n+                        listener.toByValue(),\n+                        client.toByValue(),\n+                        account.asBRCryptoAccount(),\n+                        network.asBRCryptoNetwork(),\n+                        mode,\n+                        scheme,\n+                        path\n+                )\n+        );\n     }\n \n     static CoreBRCryptoWalletManager createOwned(BRCryptoWalletManager manager) {\n@@ -60,6 +73,8 @@ public interface CoreBRCryptoWalletManager {\n \n     void sync();\n \n+    void stop();\n+\n     void syncToDepth(BRSyncDepth depth);\n \n     void submit(CoreBRCryptoWallet wallet, CoreBRCryptoTransfer transfer, byte[] phraseUtf8);\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/OwnedBRCryptoWalletManager.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/OwnedBRCryptoWalletManager.java\nindex 2bdb8e07..b1e8f1cb 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/OwnedBRCryptoWalletManager.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/crypto/OwnedBRCryptoWalletManager.java\n@@ -114,6 +114,11 @@ class OwnedBRCryptoWalletManager implements CoreBRCryptoWalletManager {\n         core.sync();\n     }\n \n+    @Override\n+    public void stop() {\n+        core.stop();\n+    }\n+\n     @Override\n     public void syncToDepth(BRSyncDepth depth) {\n         core.syncToDepth(depth);\ndiff --git a/Java/CoreNative/src/main/java/com/breadwallet/corenative/support/UInt256.java b/Java/CoreNative/src/main/java/com/breadwallet/corenative/support/UInt256.java\nindex 2294ee0b..00b7d961 100644\n--- a/Java/CoreNative/src/main/java/com/breadwallet/corenative/support/UInt256.java\n+++ b/Java/CoreNative/src/main/java/com/breadwallet/corenative/support/UInt256.java\n@@ -37,11 +37,15 @@ public class UInt256 extends Structure {\n         super(peer);\n     }\n \n-    public static class ByReference extends UInt256 implements Structure.ByReference {\n+    public ByValue toByValue() {\n+        ByValue other = new ByValue();\n+        other.u8 = this.u8;\n+        return other;\n+    }\n \n+    public static class ByReference extends UInt256 implements Structure.ByReference {\n     }\n \n     public static class ByValue extends UInt256 implements Structure.ByValue {\n-\n     }\n }\ndiff --git a/Java/Crypto/src/androidTest/java/com/breadwallet/crypto/BlockchainDbAIT.java b/Java/Crypto/src/androidTest/java/com/breadwallet/crypto/BlockchainDbAIT.java\nindex 8df14ea4..993d4a08 100644\n--- a/Java/Crypto/src/androidTest/java/com/breadwallet/crypto/BlockchainDbAIT.java\n+++ b/Java/Crypto/src/androidTest/java/com/breadwallet/crypto/BlockchainDbAIT.java\n@@ -44,8 +44,6 @@ public class BlockchainDbAIT {\n     private static final String BRD_AUTH_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJkZWI2M2UyOC0wMzQ1LTQ4ZjYtOWQxNy1jZTgwY2JkNjE3Y2IiLCJicmQ6Y3QiOiJjbGkiLCJleHAiOjkyMjMzNzIwMzY4NTQ3NzUsImlhdCI6MTU2Njg2MzY0OX0.FvLLDUSk1p7iFLJfg2kA-vwhDWTDulVjdj8YpFgnlE62OBFCYt4b3KeTND_qAhLynLKbGJ1UDpMMihsxtfvA0A";\n     private static final String API_BASE_URL = "https://stage2.breadwallet.com";\n \n-    private static final String ETH_EVENT_ERC20_TRANSFER = "0xa9059cbb";\n-\n     private BlockchainDb blockchainDb;\n \n     @Before\n@@ -223,8 +221,13 @@ public class BlockchainDbAIT {\n     public void testGetGasEstimateAsEth() {\n         SynchronousCompletionHandler<String> handler = new SynchronousCompletionHandler<>();\n \n-        blockchainDb.getGasEstimateAsEth("mainnet", "0x04d542459de6765682D21771D1ba23dC30Fb675F",\n-                "0x04d542459de6765682D21771D1ba23dC30Fb675F", "0x10000", "0x1234567890ABCDEF", handler);\n+        blockchainDb.getGasEstimateAsEth(\n+                "mainnet",\n+                "0x04d542459de6765682D21771D1ba23dC30Fb675F",\n+                "0x04d542459de6765682D21771D1ba23dC30Fb675F",\n+                "0x1000000000",\n+                "",\n+                handler);\n         String output = handler.dat().get();\n         assertFalse(output.isEmpty());\n     }\n@@ -238,7 +241,10 @@ public class BlockchainDbAIT {\n     public void testGetTransactionsAsEth() {\n         SynchronousCompletionHandler<List<EthTransaction>> handler = new SynchronousCompletionHandler<>();\n \n-        blockchainDb.getTransactionsAsEth("mainnet", "0x04d542459de6765682D21771D1ba23dC30Fb675F", UnsignedLong.ZERO,\n+        blockchainDb.getTransactionsAsEth(\n+                "mainnet",\n+                "0x04d542459de6765682D21771D1ba23dC30Fb675F",\n+                UnsignedLong.ZERO,\n                 UnsignedLong.valueOf(7778000), handler);\n         List<EthTransaction> output = handler.dat().get();\n         assertFalse(output.isEmpty());\n@@ -248,21 +254,31 @@ public class BlockchainDbAIT {\n     public void testGetLogsAsEth() {\n         SynchronousCompletionHandler<List<EthLog>> handler = new SynchronousCompletionHandler<>();\n \n-        // TODO: This test doesn\'t return anything; should it?\n-        // blockchainDb.getLogsAsEth("mainnet", null, "0x04d542459de6765682D21771D1ba23dC30Fb675F",\n-        //         ETH_EVENT_ERC20_TRANSFER, 0, 7778000, handler);\n-        // List<EthLog> output = handler.dat().get();\n-        // assertFalse(output.isEmpty());\n+         blockchainDb.getLogsAsEth(\n+                 "mainnet",\n+                 null,\n+                 "0x04d542459de6765682D21771D1ba23dC30Fb675F",\n+                 "0xa9059cbb",\n+                 UnsignedLong.ZERO,\n+                 UnsignedLong.valueOf(7778000),\n+                 handler);\n+         List<EthLog> output = handler.dat().get();\n+         assertFalse(output.isEmpty());\n     }\n \n     @Test\n     public void testGetBlocksAsEth() {\n-        SynchronousCompletionHandler<List<Long>> handler = new SynchronousCompletionHandler<>();\n-\n-        // TODO: This test doesn\'t return anything; should it?\n-        // blockchainDb.getBlocksAsEth("mainnet", "0x04d542459de6765682D21771D1ba23dC30Fb675F", 0xF, 0, 7778000, handler);\n-        // List<Long> output = handler.dat().get();\n-        // assertFalse(output.isEmpty());\n+        SynchronousCompletionHandler<List<UnsignedLong>> handler = new SynchronousCompletionHandler<>();\n+\n+        blockchainDb.getBlocksAsEth(\n+                "mainnet",\n+                "0x04d542459de6765682D21771D1ba23dC30Fb675F",\n+                UnsignedInteger.valueOf(0xF),\n+                UnsignedLong.ZERO,\n+                UnsignedLong.valueOf(7778000),\n+                handler);\n+        List<UnsignedLong> output = handler.dat().get();\n+        assertFalse(output.isEmpty());\n     }\n \n     @Test\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/Cipher.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/Cipher.java\nnew file mode 100644\nindex 00000000..f0bc5b13\n--- /dev/null\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/Cipher.java\n@@ -0,0 +1,29 @@\n+/*\n+ * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n+ * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n+*\n+ * See the LICENSE file at the project root for license information.\n+ * See the CONTRIBUTORS file at the project root for a list of contributors.\n+ */\n+package com.breadwallet.crypto;\n+\n+import com.google.common.base.Optional;\n+\n+public interface Cipher {\n+\n+    static Cipher createForAesEcb(byte[] key) {\n+        return CryptoApi.getProvider().cipherProvider().createCipherForAesEcb(key);\n+    }\n+\n+    static Cipher createForChaCha20Poly1305(Key key, byte[] nonce12, byte[] ad) {\n+        return CryptoApi.getProvider().cipherProvider().createCipherForChaCha20Poly1305(key, nonce12, ad);\n+    }\n+\n+    static Cipher createForPigeon(Key privKey, Key pubKey, byte[] nonce12) {\n+        return CryptoApi.getProvider().cipherProvider().createCipherForPigeon(privKey, pubKey, nonce12);\n+    }\n+\n+    Optional<byte[]> encrypt(byte[] data);\n+\n+    Optional<byte[]> decrypt(byte[] data);\n+}\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/Coder.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/Coder.java\nindex f955f1b3..f5b4a0ec 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/Coder.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/Coder.java\n@@ -21,7 +21,7 @@ public interface Coder {\n         return CryptoApi.getProvider().coderPrivider().createCoderForAlgorithm(algorithm);\n     }\n \n-    String encode(byte[] source);\n+    Optional<String> encode(byte[] source);\n \n     Optional<byte[]> decode(String source);\n }\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/CryptoApi.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/CryptoApi.java\nindex 581d71dc..cd6bccbb 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/CryptoApi.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/CryptoApi.java\n@@ -34,16 +34,18 @@ public final class CryptoApi {\n \n     public interface SystemProvider {\n         System create(ScheduledExecutorService executor, SystemListener listener, Account account, boolean isMainnet, String path, BlockchainDb query);\n+        void wipe(System system);\n+        void wipeAll(String path, List<System> exemptSystems);\n     }\n \n     public interface CoderProvider {\n         Coder createCoderForAlgorithm(Coder.Algorithm algorithm);\n     }\n \n-    public interface EncrypterProvider {\n-        Encrypter createEncrypterrForAesEcb(byte[] key);\n-        Encrypter createEncrypterForChaCha20Poly1305(Key key, byte[] nonce12, byte[] ad);\n-        Encrypter createEncrypterForPigeon(Key privKey, Key pubKey, byte[] nonce12);\n+    public interface CipherProvider {\n+        Cipher createCipherForAesEcb(byte[] key);\n+        Cipher createCipherForChaCha20Poly1305(Key key, byte[] nonce12, byte[] ad);\n+        Cipher createCipherForPigeon(Key privKey, Key pubKey, byte[] nonce12);\n     }\n \n     public interface HasherProvider {\n@@ -73,7 +75,7 @@ public final class CryptoApi {\n         SystemProvider systemProvider();\n \n         CoderProvider coderPrivider();\n-        EncrypterProvider encrypterProvider();\n+        CipherProvider cipherProvider();\n         HasherProvider hasherProvider();\n         KeyProvider keyProvider();\n         SignerProvider signerProvider();\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/Encrypter.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/Encrypter.java\ndeleted file mode 100644\nindex a16057e5..00000000\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/Encrypter.java\n+++ /dev/null\n@@ -1,27 +0,0 @@\n-/*\n- * Created by Michael Carrara <michael.carrara@breadwallet.com> on 7/1/19.\n- * Copyright (c) 2019 Breadwinner AG.  All right reserved.\n-*\n- * See the LICENSE file at the project root for license information.\n- * See the CONTRIBUTORS file at the project root for a list of contributors.\n- */\n-package com.breadwallet.crypto;\n-\n-public interface Encrypter {\n-\n-    static Encrypter createForAesEcb(byte[] key) {\n-        return CryptoApi.getProvider().encrypterProvider().createEncrypterrForAesEcb(key);\n-    }\n-\n-    static Encrypter createForChaCha20Poly1305(Key key, byte[] nonce12, byte[] ad) {\n-        return CryptoApi.getProvider().encrypterProvider().createEncrypterForChaCha20Poly1305(key, nonce12, ad);\n-    }\n-\n-    static Encrypter createForPigeon(Key privKey, Key pubKey, byte[] nonce12) {\n-        return CryptoApi.getProvider().encrypterProvider().createEncrypterForPigeon(privKey, pubKey, nonce12);\n-    }\n-\n-    byte[] encrypt(byte[] data);\n-\n-    byte[] decrypt(byte[] data);\n-}\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/Hasher.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/Hasher.java\nindex 2f698dc7..44e43b28 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/Hasher.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/Hasher.java\n@@ -7,6 +7,8 @@\n  */\n package com.breadwallet.crypto;\n \n+import com.google.common.base.Optional;\n+\n public interface Hasher {\n \n     enum Algorithm {\n@@ -27,5 +29,5 @@ public interface Hasher {\n         return CryptoApi.getProvider().hasherProvider().createHasherForAlgorithm(algorithm);\n     }\n \n-    byte[] hash(byte[] data);\n+    Optional<byte[]> hash(byte[] data);\n }\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/Signer.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/Signer.java\nindex 2aa11e71..afb39fa7 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/Signer.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/Signer.java\n@@ -21,7 +21,7 @@ public interface Signer {\n         return CryptoApi.getProvider().signerProvider().createSignerForAlgorithm(algorithm);\n     }\n \n-    byte[] sign(byte[] data, Key key);\n+    Optional<byte[]> sign(byte[] digest, Key key);\n \n-    Optional<? extends Key> recover(byte[] data, byte[] signature);\n+    Optional<? extends Key> recover(byte[] digest, byte[] signature);\n }\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/System.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/System.java\nindex a7cb5612..1800fb1d 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/System.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/System.java\n@@ -18,10 +18,46 @@ import java.util.concurrent.ScheduledExecutorService;\n \n public interface System {\n \n-    static System create(ScheduledExecutorService executor, SystemListener listener, Account account, boolean isMainnet, String path, BlockchainDb query) {\n-        return CryptoApi.getProvider().systemProvider().create(executor, listener, account, isMainnet,path, query);\n+    static System create(ScheduledExecutorService executor, SystemListener listener, Account account, boolean isMainnet, String storagePath, BlockchainDb query) {\n+        return CryptoApi.getProvider().systemProvider().create(executor, listener, account, isMainnet,storagePath, query);\n     }\n \n+    /**\n+     * Cease use of `system` and remove (aka \'wipe\') its persistent storage.\n+     *\n+     * Caution is highly warranted; none of the System\'s references, be they Wallet Managers,\n+     * Wallets, Transfers, etc. should be *touched* once the system is wiped.\n+     *\n+     * Note: This function blocks until completed.  Be sure that all references are dereferenced\n+     *       *before* invoking this function and remove the reference to `system` after this\n+     *       returns.\n+     */\n+    static void wipe(System system) {\n+        CryptoApi.getProvider().systemProvider().wipe(system);\n+    }\n+\n+    /**\n+     * Remove (aka \'wipe\') the persistent storage associated with any and all systems located\n+     * within `atPath` except for a specified array of systems to preserve.  Generally, this\n+     * function should be called on startup after all systems have been created.  When called at\n+     * that time, any \'left over\' systems will have their persistent storeage wiped.\n+     *\n+     * @param storagePath the file system path where system data is persistently stored\n+     * @param exemptSystems the list of systems that should not have their data wiped.\n+     */\n+    static void wipeAll(String storagePath, List<System> exemptSystems) {\n+        CryptoApi.getProvider().systemProvider().wipeAll(storagePath, exemptSystems);;\n+    }\n+\n+    /**\n+     * Configure the system.  This will query various BRD services, notably the BlockChainDB, to\n+     * establish the available networks (aka blockchains) and their currencies.  For each\n+     * `Network` there will be `SystemEvent` which can be used by the App to create a\n+     * `WalletManager`.\n+     *\n+     * @param appCurrencies If the BlockChainDB does not return any currencies, then\n+     *                      use `applicationCurrencies` merged into the defaults.\n+     */\n     void configure(List<com.breadwallet.crypto.blockchaindb.models.bdb.Currency> appCurrencies);\n \n     /**\n@@ -43,7 +79,17 @@ public interface System {\n                              AddressScheme addressScheme,\n                              Set<Currency> currencies);\n \n-    void stop();\n+    /**\n+     * Connect all wallet managers.\n+     *\n+     * They will be connected w/o an explict NetworkPeer.\n+     */\n+    void connectAll();\n+\n+    /**\n+     * Disconnect all wallet managers.\n+     */\n+    void disconnectAll();\n \n     void subscribe(String subscriptionToken);\n \ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/WalletManager.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/WalletManager.java\nindex 836b1265..e05f4031 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/WalletManager.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/WalletManager.java\n@@ -27,6 +27,8 @@ public interface WalletManager {\n \n     void sync();\n \n+    void stop();\n+\n     void syncToDepth(WalletManagerSyncDepth depth);\n \n     void submit(Transfer transfer, byte[] phraseUtf8);\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/BlockchainDb.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/BlockchainDb.java\nindex e77020db..186cbf62 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/BlockchainDb.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/BlockchainDb.java\n@@ -107,7 +107,7 @@ public class BlockchainDb {\n         this.ethBlockApi = new EthBlockApi(brdClient);\n         this.ethGasApi = new EthGasApi(brdClient);\n         this.ethTokenApi = new EthTokenApi(brdClient);\n-        this.ethTransferApi = new EthTransferApi(brdClient, executorService);\n+        this.ethTransferApi = new EthTransferApi(brdClient);\n     }\n \n     public static BlockchainDb createForTest (OkHttpClient client,\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/bdb/BlockApi.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/bdb/BlockApi.java\nindex 80db9915..576f8e03 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/bdb/BlockApi.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/bdb/BlockApi.java\n@@ -20,14 +20,9 @@ import com.google.common.primitives.UnsignedLong;\n import java.util.ArrayList;\n import java.util.List;\n import java.util.concurrent.ExecutorService;\n-import java.util.concurrent.Semaphore;\n-\n-import static com.google.common.base.Preconditions.checkArgument;\n \n public class BlockApi {\n \n-    private static final UnsignedLong PAGINATION_COUNT = UnsignedLong.valueOf(5000);\n-\n     private final BdbApiClient jsonClient;\n     private final ExecutorService executorService;\n \n@@ -39,8 +34,18 @@ public class BlockApi {\n     public void getBlocks(String id, UnsignedLong beginBlockNumber, UnsignedLong endBlockNumber, boolean includeRaw,\n                           boolean includeTx, boolean includeTxRaw, boolean includeTxProof,\n                           CompletionHandler<List<Block>, QueryError> handler) {\n-        executorService.submit(() -> getBlocksOnExecutor(id, beginBlockNumber, endBlockNumber, includeRaw, includeTx,\n-                includeTxRaw, includeTxProof, handler));\n+        ImmutableListMultimap.Builder<String, String> paramsBuilder = ImmutableListMultimap.builder();\n+        paramsBuilder.put("blockchain_id", id);\n+        paramsBuilder.put("include_raw", String.valueOf(includeRaw));\n+        paramsBuilder.put("include_tx", String.valueOf(includeTx));\n+        paramsBuilder.put("include_tx_raw", String.valueOf(includeTxRaw));\n+        paramsBuilder.put("include_tx_proof", String.valueOf(includeTxProof));\n+        paramsBuilder.put("start_height", beginBlockNumber.toString());\n+        paramsBuilder.put("end_height", endBlockNumber.toString());\n+        ImmutableMultimap<String, String> params = paramsBuilder.build();\n+\n+        PagedCompletionHandler<List<Block>, QueryError> pagedHandler = createPagedResultsHandler(handler);\n+        jsonClient.sendGetForArrayWithPaging("blocks", params, Block::asBlocks, pagedHandler);\n     }\n \n     public void getBlock(String id, boolean includeRaw,\n@@ -55,67 +60,32 @@ public class BlockApi {\n         jsonClient.sendGetWithId("blocks", id, params, Block::asBlock, handler);\n     }\n \n-    private void getBlocksOnExecutor(String id, UnsignedLong beginBlockNumber, UnsignedLong endBlockNumber, boolean includeRaw,\n-                                     boolean includeTx, boolean includeTxRaw, boolean includeTxProof,\n-                                     CompletionHandler<List<Block>, QueryError> handler) {\n-        final QueryError[] error = {null};\n-        List<Block> allBlocks = new ArrayList<>();\n-        Semaphore sema = new Semaphore(0);\n-\n-        ImmutableListMultimap.Builder<String, String> paramsBuilder = ImmutableListMultimap.builder();\n-        paramsBuilder.put("blockchain_id", id);\n-        paramsBuilder.put("include_raw", String.valueOf(includeRaw));\n-        paramsBuilder.put("include_tx", String.valueOf(includeTx));\n-        paramsBuilder.put("include_tx_raw", String.valueOf(includeTxRaw));\n-        paramsBuilder.put("include_tx_proof", String.valueOf(includeTxProof));\n-        paramsBuilder.put("start_height", beginBlockNumber.toString());\n-        paramsBuilder.put("end_height", endBlockNumber.toString());\n-        ImmutableMultimap<String, String> params = paramsBuilder.build();\n+    private void submitGetNextBlocks(String nextUrl, PagedCompletionHandler<List<Block>, QueryError> handler) {\n+        executorService.submit(() -> getNextBlocks(nextUrl, handler));\n+    }\n \n-        final String[] nextUrl = {null};\n+    private void getNextBlocks(String nextUrl, PagedCompletionHandler<List<Block>, QueryError> handler) {\n+        jsonClient.sendGetForArrayWithPaging("blocks", nextUrl, Block::asBlocks, handler);\n+    }\n \n-        jsonClient.sendGetForArrayWithPaging("blocks", params, Block::asBlocks,\n-                new PagedCompletionHandler<List<Block>, QueryError>() {\n+    private PagedCompletionHandler<List<Block>, QueryError> createPagedResultsHandler(CompletionHandler<List<Block>, QueryError> handler) {\n+        List<Block> allResults = new ArrayList<>();\n+        return new PagedCompletionHandler<List<Block>, QueryError>() {\n             @Override\n-            public void handleData(List<Block> blocks, PageInfo info) {\n-                nextUrl[0] = info.nextUrl;\n-                allBlocks.addAll(blocks);\n-                sema.release();\n+            public void handleData(List<Block> results, PageInfo info) {\n+                allResults.addAll(results);\n+\n+                if (info.nextUrl == null) {\n+                    handler.handleData(allResults);\n+                } else {\n+                    submitGetNextBlocks(info.nextUrl, this);\n+                }\n             }\n \n             @Override\n-            public void handleError(QueryError e) {\n-                error[0] = e;\n-                sema.release();\n+            public void handleError(QueryError error) {\n+                handler.handleError(error);\n             }\n-        });\n-\n-        sema.acquireUninterruptibly();\n-\n-        while (nextUrl[0] != null && error[0] == null) {\n-            jsonClient.sendGetForArrayWithPaging("blocks", nextUrl[0], Block::asBlocks,\n-                    new PagedCompletionHandler<List<Block>, QueryError>() {\n-                        @Override\n-                        public void handleData(List<Block> blocks, PageInfo info) {\n-                            nextUrl[0] = info.nextUrl;\n-                            allBlocks.addAll(blocks);\n-                            sema.release();\n-                        }\n-\n-                        @Override\n-                        public void handleError(QueryError e) {\n-                            error[0] = e;\n-                            sema.release();\n-                        }\n-                    });\n-\n-            sema.acquireUninterruptibly();\n-        }\n-\n-        if (error[0] != null) {\n-            handler.handleError(error[0]);\n-        } else {\n-            handler.handleData(allBlocks);\n-        }\n+        };\n     }\n }\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/bdb/TransactionApi.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/bdb/TransactionApi.java\nindex 1dae90e3..705d7431 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/bdb/TransactionApi.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/bdb/TransactionApi.java\n@@ -25,11 +25,11 @@ import org.json.JSONObject;\n import java.util.ArrayList;\n import java.util.List;\n import java.util.concurrent.ExecutorService;\n-import java.util.concurrent.Semaphore;\n+\n+import static com.google.common.base.Preconditions.checkState;\n \n public class TransactionApi {\n \n-    private static final UnsignedLong PAGINATION_COUNT = UnsignedLong.valueOf(5000);\n     private static final int ADDRESS_COUNT = 50;\n \n     private final BdbApiClient jsonClient;\n@@ -43,8 +43,24 @@ public class TransactionApi {\n     public void getTransactions(String id, List<String> addresses, UnsignedLong beginBlockNumber, UnsignedLong endBlockNumber,\n                                 boolean includeRaw, boolean includeProof,\n                                 CompletionHandler<List<Transaction>, QueryError> handler) {\n-        executorService.submit(() -> getTransactionsOnExecutor(id, addresses, beginBlockNumber, endBlockNumber,\n-                includeRaw, includeProof, handler));\n+        List<List<String>> chunkedAddressesList = Lists.partition(addresses, ADDRESS_COUNT);\n+        GetTransactionsCoordinator coordinator = new GetTransactionsCoordinator(chunkedAddressesList, handler);\n+\n+        for (int i = 0; i < chunkedAddressesList.size(); i++) {\n+            List<String> chunkedAddresses = chunkedAddressesList.get(i);\n+\n+            ImmutableListMultimap.Builder<String, String> paramsBuilder = ImmutableListMultimap.builder();\n+            paramsBuilder.put("blockchain_id", id);\n+            paramsBuilder.put("include_proof", String.valueOf(includeProof));\n+            paramsBuilder.put("include_raw", String.valueOf(includeRaw));\n+            paramsBuilder.put("start_height", beginBlockNumber.toString());\n+            paramsBuilder.put("end_height", endBlockNumber.toString());\n+            for (String address : chunkedAddresses) paramsBuilder.put("address", address);\n+            ImmutableMultimap<String, String> params = paramsBuilder.build();\n+\n+            PagedCompletionHandler<List<Transaction>, QueryError> pagedHandler = coordinator.createPagedResultsHandler(chunkedAddresses);\n+            jsonClient.sendGetForArrayWithPaging("transactions", params, Transaction::asTransactions, pagedHandler);\n+        }\n     }\n \n     public void getTransaction(String id, boolean includeRaw, boolean includeProof,\n@@ -63,71 +79,99 @@ public class TransactionApi {\n         jsonClient.sendPost("transactions", ImmutableMultimap.of(), json, handler);\n     }\n \n-    private void getTransactionsOnExecutor(String id, List<String> addresses, UnsignedLong beginBlockNumber,\n-                                           UnsignedLong endBlockNumber, boolean includeRaw, boolean includeProof,\n-                                           CompletionHandler<List<Transaction>, QueryError> handler) {\n-        final QueryError[] error = {null};\n-        List<Transaction> allTransactions = new ArrayList<>();\n-        Semaphore sema = new Semaphore(0);\n+    private void submitGetNextTransactions(String nextUrl, PagedCompletionHandler<List<Transaction>, QueryError> handler) {\n+        executorService.submit(() -> getNextTransactions(nextUrl, handler));\n+    }\n \n-        List<List<String>> chunkedAddressesList = Lists.partition(addresses, ADDRESS_COUNT);\n-        for (int i = 0; i < chunkedAddressesList.size() && error[0] == null; i++) {\n-            List<String> chunkedAddresses = chunkedAddressesList.get(i);\n+    private void getNextTransactions(String nextUrl, PagedCompletionHandler<List<Transaction>, QueryError> handler) {\n+        jsonClient.sendGetForArrayWithPaging("transactions", nextUrl, Transaction::asTransactions,\n+                handler);\n+    }\n \n-            ImmutableListMultimap.Builder<String, String> paramsBuilder = ImmutableListMultimap.builder();\n-            paramsBuilder.put("blockchain_id", id);\n-            paramsBuilder.put("include_proof", String.valueOf(includeProof));\n-            paramsBuilder.put("include_raw", String.valueOf(includeRaw));\n-            paramsBuilder.put("start_height", beginBlockNumber.toString());\n-            paramsBuilder.put("end_height", endBlockNumber.toString());\n-            for (String address : chunkedAddresses) paramsBuilder.put("address", address);\n-            ImmutableMultimap<String, String> params = paramsBuilder.build();\n+    private class GetTransactionsCoordinator {\n+\n+        private final List<List<String>> chunks;\n+        private final List<Transaction> transactions;\n+        private final CompletionHandler<List<Transaction>, QueryError> handler;\n+\n+        private QueryError error;\n \n-            final String[] nextUrl = {null};\n-\n-            jsonClient.sendGetForArrayWithPaging("transactions", params, Transaction::asTransactions,\n-                    new PagedCompletionHandler<List<Transaction>, QueryError>() {\n-                        @Override\n-                        public void handleData(List<Transaction> transactions, PageInfo info) {\n-                            nextUrl[0] = info.nextUrl;\n-                            allTransactions.addAll(transactions);\n-                            sema.release();\n-                        }\n-\n-                        @Override\n-                        public void handleError(QueryError e) {\n-                            error[0] = e;\n-                            sema.release();\n-                        }\n-                    });\n-\n-            sema.acquireUninterruptibly();\n-\n-            while (nextUrl[0] != null && error[0] == null) {\n-                jsonClient.sendGetForArrayWithPaging("transactions", nextUrl[0], Transaction::asTransactions,\n-                        new PagedCompletionHandler<List<Transaction>, QueryError>() {\n-                            @Override\n-                            public void handleData(List<Transaction> transactions, PageInfo info) {\n-                                nextUrl[0] = info.nextUrl;\n-                                allTransactions.addAll(transactions);\n-                                sema.release();\n-                            }\n-\n-                            @Override\n-                            public void handleError(QueryError e) {\n-                                error[0] = e;\n-                                sema.release();\n-                            }\n-                        });\n-\n-                sema.acquireUninterruptibly();\n+        private GetTransactionsCoordinator(List<List<String>> chunks, CompletionHandler<List<Transaction>, QueryError> handler) {\n+            this.chunks = new ArrayList<>(chunks);\n+            this.transactions = new ArrayList<>();\n+            this.handler = handler;\n+        }\n+\n+        private PagedCompletionHandler<List<Transaction>, QueryError> createPagedResultsHandler(List<String> chunk) {\n+            List<Transaction> allResults = new ArrayList<>();\n+            return new PagedCompletionHandler<List<Transaction>, QueryError>() {\n+                @Override\n+                public void handleData(List<Transaction> results, PageInfo info) {\n+                    allResults.addAll(results);\n+\n+                    if (info.nextUrl == null) {\n+                        GetTransactionsCoordinator.this.handleChunkData(chunk, allResults);\n+                    } else {\n+                        submitGetNextTransactions(info.nextUrl, this);\n+                    }\n+                }\n+\n+                @Override\n+                public void handleError(QueryError error) {\n+                    GetTransactionsCoordinator.this.handleError(error);\n+                }\n+            };\n+        }\n+\n+        private void handleChunkData(List<String> chunk, List<Transaction> data) {\n+            boolean transitionToSuccess = false;\n+\n+            synchronized (this) {\n+                checkState(!isInSuccessState());\n+\n+                if (!isInErrorState()) {\n+                    chunks.remove(chunk);\n+                    transactions.addAll(data);\n+                    transitionToSuccess = isInSuccessState();\n+                }\n             }\n+\n+            if (transitionToSuccess) {\n+                handleSuccess();\n+            }\n+        }\n+\n+        private void handleError(QueryError error) {\n+            boolean transitionToError = false;\n+\n+            synchronized (this) {\n+                checkState(!isInSuccessState());\n+\n+                if (!isInErrorState()) {\n+                    this.error = error;\n+                    transitionToError = isInErrorState();\n+                }\n+            }\n+\n+            if (transitionToError) {\n+                handleFailure(error);\n+            }\n+        }\n+\n+        private boolean isInErrorState() {\n+            return error != null;\n+        }\n+\n+        private boolean isInSuccessState() {\n+            return chunks.isEmpty();\n+        }\n+\n+        private void handleSuccess() {\n+            handler.handleData(transactions);\n         }\n \n-        if (error[0] != null) {\n-            handler.handleError(error[0]);\n-        } else {\n-            handler.handleData(allTransactions);\n+        private void handleFailure(QueryError error) {\n+            handler.handleError(error);\n         }\n     }\n }\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/brd/EthBlockApi.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/brd/EthBlockApi.java\nindex 10546766..7de8ba76 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/brd/EthBlockApi.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/brd/EthBlockApi.java\n@@ -8,6 +8,7 @@\n package com.breadwallet.crypto.blockchaindb.apis.brd;\n \n import com.breadwallet.crypto.blockchaindb.errors.QueryError;\n+import com.breadwallet.crypto.blockchaindb.errors.QueryNoDataError;\n import com.breadwallet.crypto.utility.CompletionHandler;\n import com.google.common.collect.ImmutableList;\n import com.google.common.collect.ImmutableMap;\n@@ -30,6 +31,23 @@ public class EthBlockApi {\n                 "id", rid\n         ));\n \n-        client.sendJsonRequest(networkName, json, handler);\n+        client.sendJsonRequest(networkName, json, new CompletionHandler<String, QueryError>() {\n+            @Override\n+            public void handleData(String data) {\n+                // If we get a successful response, but the provided blocknumber is "0" then\n+                // that indicates that the JSON-RPC node is syncing.  Thus, if "0" transform\n+                // to a .failure\n+                if (!"0".equals(data) && !"0x".equals(data)) {\n+                    handler.handleData(data);\n+                } else {\n+                    handler.handleError(new QueryNoDataError());\n+                }\n+            }\n+\n+            @Override\n+            public void handleError(QueryError error) {\n+                handler.handleError(error);\n+            }\n+        });\n     }\n }\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/brd/EthTransferApi.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/brd/EthTransferApi.java\nindex be4cf5ae..fbeb9a06 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/brd/EthTransferApi.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/apis/brd/EthTransferApi.java\n@@ -26,19 +26,17 @@ import org.json.JSONObject;\n \n import java.util.ArrayList;\n import java.util.List;\n-import java.util.concurrent.ExecutorService;\n-import java.util.concurrent.Semaphore;\n+\n+import static com.google.common.base.Preconditions.checkState;\n \n public class EthTransferApi {\n \n     private static final String ETH_EVENT_ERC20_TRANSFER = "0xa9059cbb";\n \n     private final BrdApiClient client;\n-    private final ExecutorService executorService;\n \n-    public EthTransferApi(BrdApiClient client, ExecutorService executorService) {\n+    public EthTransferApi(BrdApiClient client) {\n         this.client = client;\n-        this.executorService = executorService;\n     }\n \n     public void submitTransactionAsEth(String networkName, String transaction, int rid,\n@@ -109,60 +107,123 @@ public class EthTransferApi {\n \n     public void getBlocksAsEth(String networkName, String address, UnsignedInteger interests, UnsignedLong blockStart, UnsignedLong blockEnd,\n                                int rid, CompletionHandler<List<UnsignedLong>, QueryError> handler) {\n-        executorService.submit(() -> getBlocksAsEthOnExecutor(networkName, address, interests, blockStart, blockEnd,\n-                rid, handler));\n+        GetBlocksCoordinator coordinator = new GetBlocksCoordinator(address, interests, handler);\n+\n+        getTransactionsAsEth(networkName, address, blockStart, blockEnd, rid, coordinator.createTxnHandler());\n+        getLogsAsEth(networkName, null, address, ETH_EVENT_ERC20_TRANSFER, blockStart, blockEnd, rid, coordinator.createLogHandler());\n     }\n \n-    private void getBlocksAsEthOnExecutor(String networkName, String address, UnsignedInteger interests, UnsignedLong blockStart,\n-                                          UnsignedLong blockEnd, int rid, CompletionHandler<List<UnsignedLong>, QueryError> handler) {\n-        final QueryError[] error = {null};\n-\n-        List<EthTransaction> transactions = new ArrayList<>();\n-        Semaphore transactionsSema = new Semaphore(0);\n-        getTransactionsAsEth(networkName, address, blockStart, blockEnd, rid, new CompletionHandler<List<EthTransaction>, QueryError>() {\n-            @Override\n-            public void handleData(List<EthTransaction> data) {\n-                transactions.addAll(data);\n-                transactionsSema.release();\n+    private static class GetBlocksCoordinator {\n+\n+        private final String address;\n+        private final UnsignedInteger interests;\n+        private final CompletionHandler<List<UnsignedLong>, QueryError> handler;\n+\n+        private List<EthTransaction> transactions;\n+        private List<EthLog> logs;\n+        private QueryError error;\n+\n+        private GetBlocksCoordinator(String address, UnsignedInteger interests, CompletionHandler<List<UnsignedLong>, QueryError> handler) {\n+            this.address = address;\n+            this.interests = interests;\n+            this.handler = handler;\n+        }\n+\n+        private CompletionHandler<List<EthTransaction>, QueryError> createTxnHandler() {\n+            return new CompletionHandler<List<EthTransaction>, QueryError>() {\n+                @Override\n+                public void handleData(List<EthTransaction> data) {\n+                    GetBlocksCoordinator.this.handleTxnData(data);\n+                }\n+\n+                @Override\n+                public void handleError(QueryError e) {\n+                    GetBlocksCoordinator.this.handleError(e);\n+                }\n+            };\n+        }\n+\n+        private CompletionHandler<List<EthLog>, QueryError> createLogHandler() {\n+            return new CompletionHandler<List<EthLog>, QueryError>() {\n+                @Override\n+                public void handleData(List<EthLog> data) {\n+                    GetBlocksCoordinator.this.handleLogData(data);\n+                }\n+\n+                @Override\n+                public void handleError(QueryError e) {\n+                    GetBlocksCoordinator.this.handleError(e);\n+                }\n+            };\n+        }\n+\n+        private void handleTxnData(List<EthTransaction> txns) {\n+            boolean transitionToSuccess = false;\n+\n+            synchronized (this) {\n+                checkState(!isInSuccessState());\n+\n+                if (!isInErrorState()) {\n+                    this.transactions = txns;\n+                    transitionToSuccess = isInSuccessState();\n+                }\n             }\n \n-            @Override\n-            public void handleError(QueryError e) {\n-                error[0] = e;\n-                transactionsSema.release();\n+            if (transitionToSuccess) {\n+                handleSuccess();\n             }\n-        });\n-\n-        List<EthLog> logs = new ArrayList<>();\n-        Semaphore logsSema = new Semaphore(0);\n-        getLogsAsEth(networkName, null, address, ETH_EVENT_ERC20_TRANSFER, blockStart, blockEnd, rid, new CompletionHandler<List<EthLog>, QueryError>() {\n-            @Override\n-            public void handleData(List<EthLog> data) {\n-                logs.addAll(data);\n-                logsSema.release();\n+        }\n+\n+        private void handleLogData(List<EthLog> logs) {\n+            boolean transitionToSuccess = false;\n+\n+            synchronized (this) {\n+                checkState(!isInSuccessState());\n+\n+                if (!isInErrorState()) {\n+                    this.logs = logs;\n+                    transitionToSuccess = isInSuccessState();\n+                }\n             }\n \n-            @Override\n-            public void handleError(QueryError e) {\n-                error[0] = e;\n-                logsSema.release();\n+            if (transitionToSuccess) {\n+                handleSuccess();\n             }\n-        });\n+        }\n+\n+        private void handleError(QueryError error) {\n+            boolean transitionToError = false;\n \n-        transactionsSema.acquireUninterruptibly();\n-        logsSema.acquireUninterruptibly();\n+            synchronized (this) {\n+                checkState(!isInSuccessState());\n \n-        if (error[0] != null) {\n-            handler.handleError(error[0]);\n+                if (!isInErrorState()) {\n+                    this.error = error;\n+                    transitionToError = isInErrorState();\n+                }\n+            }\n \n-        } else {\n+            if (transitionToError) {\n+                handleFailure(error);\n+            }\n+        }\n+\n+        private boolean isInErrorState() {\n+            return error != null;\n+        }\n+\n+        private boolean isInSuccessState() {\n+            return transactions != null && logs != null;\n+        }\n+\n+        private void handleSuccess() {\n             List<UnsignedLong> numbers = new ArrayList<>();\n             int interestsAsInt = interests.intValue();\n \n-            for (EthTransaction transaction: transactions) {\n+            for (EthTransaction transaction : transactions) {\n \n-                boolean include = (0 != (interestsAsInt & (1 << 0)) && address.equals(transaction.getSourceAddr())) ||\n-                        (0 != (interestsAsInt & (1 << 1)) && address.equals(transaction.getTargetAddr()));\n+                boolean include = (0 != (interestsAsInt & (1 << 0)) && address.equalsIgnoreCase(transaction.getSourceAddr())) ||\n+                        (0 != (interestsAsInt & (1 << 1)) && address.equalsIgnoreCase(transaction.getTargetAddr()));\n                 if (include) {\n                     try {\n                         numbers.add(UnsignedLong.fromLongBits(UnsignedLongs.decode(transaction.getBlockNumber())));\n@@ -173,11 +234,11 @@ public class EthTransferApi {\n                 }\n             }\n \n-            for (EthLog log: logs) {\n+            for (EthLog log : logs) {\n                 List<String> topics = log.getTopics();\n                 boolean include = topics.size() == 3 &&\n-                        ((0 != (interestsAsInt & (1 << 2)) && address.equals(topics.get(1))) ||\n-                                (0 != (interestsAsInt & (1 << 3)) && address.equals(topics.get(2))));\n+                        ((0 != (interestsAsInt & (1 << 2)) && address.equalsIgnoreCase(topics.get(1))) ||\n+                                (0 != (interestsAsInt & (1 << 3)) && address.equalsIgnoreCase(topics.get(2))));\n                 if (include) {\n                     try {\n                         numbers.add(UnsignedLong.fromLongBits(UnsignedLongs.decode(log.getBlockNumber())));\n@@ -190,5 +251,9 @@ public class EthTransferApi {\n \n             handler.handleData(numbers);\n         }\n+\n+        private void handleFailure(QueryError error) {\n+            handler.handleError(error);\n+        }\n     }\n }\ndiff --git a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/models/Utilities.java b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/models/Utilities.java\nindex eb9dce5f..97a5bd76 100644\n--- a/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/models/Utilities.java\n+++ b/Java/Crypto/src/main/java/com/breadwallet/crypto/blockchaindb/models/Utilities.java\n@@ -28,11 +28,15 @@ import java.util.Locale;\n \n public final class Utilities {\n \n-    private static DateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd\'T\'HH:mm:ss.SSSZ", Locale.US);\n+    private static final ThreadLocal<DateFormat> ISO_8601_FORMAT = new ThreadLocal<DateFormat>() {\n+        @Override protected DateFormat initialValue() {\n+            return new SimpleDateFormat("yyyy-MM-dd\'T\'HH:mm:ss.SSSZ", Locale.US);\n+        }\n+    };\n \n     public static Date get8601DateFromString(JSONObject json, String name) throws JSONException {\n         try {\n-            return ISO_8601_FORMAT.parse(json.getString(name));\n+            return ISO_8601_FORMAT.get().parse(json.getString(name));\n         } catch (ParseException e) {\n             throw new JSONException("Invalid date value for " + name);\n         }\n@@ -40,14 +44,14 @@ public final class Utilities {\n \n     public static Optional<Date> getOptional8601DateFromString(JSONObject json, String name) {\n         try {\n-            return Optional.fromNullable(ISO_8601_FORMAT.parse(json.optString(name, null)));\n+            return Optional.fromNullable(ISO_8601_FORMAT.get().parse(json.optString(name, null)));\n         } catch (ParseException e) {\n             return Optional.absent();\n         }\n     }\n \n     public  static String get8601StringFromDate(Date date) {\n-        return ISO_8601_FORMAT.format(date);\n+        return ISO_8601_FORMAT.get().format(date);\n     }\n \n     public static Optional<byte[]> getOptionalBase64Bytes(JSONObject json, String name) {\ndiff --git a/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/CoreCryptoApplication.java b/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/CoreCryptoApplication.java\nindex aac7a629..0e570269 100644\n--- a/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/CoreCryptoApplication.java\n+++ b/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/CoreCryptoApplication.java\n@@ -92,6 +92,12 @@ public class CoreCryptoApplication extends Application {\n         instance.resetSystemImpl();\n     }\n \n+    public static void wipeSystem() {\n+        checkState(null != instance && instance.runOnce.get());\n+\n+        instance.wipeSystemImpl();\n+    }\n+\n     public static DispatchingSystemListener getDispatchingSystemListener() {\n         checkState(null != instance && instance.runOnce.get());\n \n@@ -110,18 +116,6 @@ public class CoreCryptoApplication extends Application {\n         return instance.executor;\n     }\n \n-    private static void deleteRecursively (File file) {\n-        if (file.isDirectory()) {\n-            for (File child : file.listFiles()) {\n-                deleteRecursively(child);\n-            }\n-        }\n-\n-        if (file.exists() && !file.delete()) {\n-            Log.e(TAG, "Failed to delete " + file.getAbsolutePath());\n-        }\n-    }\n-\n     @Override\n     public void onCreate() {\n         super.onCreate();\n@@ -141,10 +135,7 @@ public class CoreCryptoApplication extends Application {\n             WalletManagerMode mode = intent.hasExtra(EXTRA_MODE) ? WalletManagerMode.valueOf(intent.getStringExtra(EXTRA_MODE)) : DEFAULT_MODE;\n \n             storageFile = new File(getFilesDir(), "core");\n-            if (wipe) {\n-                if (storageFile.exists()) deleteRecursively(storageFile);\n-                checkState(storageFile.mkdirs());\n-            }\n+            if (!storageFile.exists()) checkState(storageFile.mkdirs());\n \n             Log.d(TAG, String.format("Account PaperKey:  %s", paperKeyString));\n             Log.d(TAG, String.format("Account Timestamp: %s", timestamp));\n@@ -166,13 +157,39 @@ public class CoreCryptoApplication extends Application {\n                     isMainnet, storageFile.getAbsolutePath(), blockchainDb);\n             system.configure(Collections.emptyList());\n \n+            System.wipeAll(storageFile.getAbsolutePath(), wipe ? Collections.emptyList() : Collections.singletonList(system));\n+\n             connectivityReceiver = new ConnectivityBroadcastReceiver();\n             registerReceiver(connectivityReceiver , new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));\n         }\n     }\n \n     private void resetSystemImpl() {\n-        system.stop();\n+        Log.d(TAG, "Resetting");\n+\n+        // Wipe the current system.\n+        System.wipe(system);\n+\n+        // Create a new system\n+        system = System.create(\n+                executor,\n+                systemListener,\n+                account,\n+                isMainnet,\n+                storageFile.getAbsolutePath(),\n+                blockchainDb);\n+\n+        // Passing empty list... it is a demo app...\n+        system.configure(Collections.emptyList());\n+    }\n+\n+    private void wipeSystemImpl() {\n+        Log.d(TAG, "Wiping");\n+\n+        // Wipe the current system.\n+        System.wipe(system);\n+\n+        // Create a new system\n         system = System.create(\n                 executor,\n                 systemListener,\n@@ -180,6 +197,8 @@ public class CoreCryptoApplication extends Application {\n                 isMainnet,\n                 storageFile.getAbsolutePath(),\n                 blockchainDb);\n+\n+        // Passing empty list... it is a demo app...\n         system.configure(Collections.emptyList());\n     }\n }\ndiff --git a/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/TransferDetailsActivity.java b/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/TransferDetailsActivity.java\nindex 6e1c3814..a7b2078e 100644\n--- a/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/TransferDetailsActivity.java\n+++ b/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/TransferDetailsActivity.java\n@@ -41,8 +41,6 @@ import java.text.DateFormat;\n \n public class TransferDetailsActivity extends AppCompatActivity {\n \n-    private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);\n-\n     private static final String EXTRA_WALLET_NAME = "com.breadwallet.cryptodemo,TransferDetailsActivity.EXTRA_WALLET_NAME";\n     private static final String EXTRA_TXN_ID = "com.breadwallet.cryptodemo,TransferDetailsActivity.EXTRA_TXN_ID";\n \n@@ -183,7 +181,9 @@ public class TransferDetailsActivity extends AppCompatActivity {\n         feeView.setText(feeText);\n         feeView.setOnClickListener(v -> copyPlaintext("Fee", feeView.getText()));\n \n-        String dateText = confirmation.transform((c) -> DATE_FORMAT.format(c.getConfirmationTime())).or("<pending>");\n+        String dateText = confirmation\n+                .transform((c) -> DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(c.getConfirmationTime()))\n+                .or("<pending>");\n         dateView.setText(dateText);\n \n         String senderText = transfer.getSource().transform(Address::toString).or("<unknown>");\ndiff --git a/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/TransferListActivity.java b/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/TransferListActivity.java\nindex 21cd694d..c5a08047 100644\n--- a/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/TransferListActivity.java\n+++ b/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/TransferListActivity.java\n@@ -61,8 +61,6 @@ import java.util.Objects;\n \n public class TransferListActivity extends AppCompatActivity {\n \n-    private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);\n-\n     private static final String EXTRA_WALLET_NAME = "com.breadwallet.cryptodemo.TransferListActivity.EXTRA_WALLET_NAME";\n \n     public static void start(Activity callerActivity, Wallet wallet) {\n@@ -385,7 +383,8 @@ public class TransferListActivity extends AppCompatActivity {\n             TransferViewModel transfer = viewModels.get(i);\n \n             String dateText = transfer.getConfirmation()\n-                    .transform((c) -> DATE_FORMAT.format(c.getConfirmationTime())).or("<pending>");\n+                    .transform((c) -> DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(c.getConfirmationTime()))\n+                    .or("<pending>");\n \n             String addressText = transfer.getHash().transform(TransferHash::toString).or("<pending>");\n             addressText = String.format("Hash: %s", addressText);\ndiff --git a/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/WalletListActivity.java b/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/WalletListActivity.java\nindex e94c32aa..2d6c721a 100644\n--- a/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/WalletListActivity.java\n+++ b/Java/CryptoDemo/src/main/java/com/breadwallet/cryptodemo/WalletListActivity.java\n@@ -8,6 +8,7 @@\n package com.breadwallet.cryptodemo;\n \n import android.support.annotation.NonNull;\n+import android.support.v7.app.AlertDialog;\n import android.support.v7.app.AppCompatActivity;\n import android.os.Bundle;\n import android.support.v7.util.SortedList;\n@@ -24,6 +25,7 @@ import android.view.ViewGroup;\n import android.widget.TextView;\n \n import com.breadwallet.crypto.Amount;\n+import com.breadwallet.crypto.Currency;\n import com.breadwallet.crypto.Network;\n import com.breadwallet.crypto.System;\n import com.breadwallet.crypto.Wallet;\n@@ -37,8 +39,11 @@ import com.breadwallet.crypto.events.wallet.WalletDeletedEvent;\n import com.breadwallet.crypto.events.wallet.WalletEvent;\n \n import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.HashSet;\n import java.util.List;\n import java.util.Objects;\n+import java.util.Set;\n \n public class WalletListActivity extends AppCompatActivity implements DefaultSystemListener {\n \n@@ -88,10 +93,45 @@ public class WalletListActivity extends AppCompatActivity implements DefaultSyst\n     @Override\n     public boolean onOptionsItemSelected(MenuItem item) {\n         switch (item.getItemId()) {\n-            case R.id.action_connect:\n-                for (WalletManager wm: CoreCryptoApplication.getSystem().getWalletManagers()) {\n-                    wm.connect(null);\n+            case R.id.action_add_wallet:\n+                AlertDialog dialog;\n+\n+                List<Currency> missingCurrencies = getMissingCurrencies();\n+                if (missingCurrencies.isEmpty()) {\n+                    // all currencies have wallets; ignore\n+                    dialog = new AlertDialog.Builder(this)\n+                            .setMessage("All currencies added and accounted for!")\n+                            .create();\n+\n+                } else {\n+                    // extract the currencies and their descriptions\n+                    String[] itemTexts = new String[missingCurrencies.size()];\n+                    Currency[] itemCurrencies = new Currency[missingCurrencies.size()];\n+                    for (int i = 0; i < missingCurrencies.size(); i++) {\n+                        itemCurrencies[i] = missingCurrencies.get(i);\n+                        itemTexts[i] = itemCurrencies[i].getCode();\n+                    }\n+\n+                    // prompt for addition\n+                    dialog = new AlertDialog.Builder(this)\n+                            .setSingleChoiceItems(itemTexts,\n+                                    -1,\n+                                    (d, w) -> {\n+                                        for (WalletManager manager : CoreCryptoApplication.getSystem().getWalletManagers()) {\n+                                            if (manager.getNetwork().hasCurrency(itemCurrencies[w])) {\n+                                                manager.registerWalletFor(itemCurrencies[w]);\n+                                                break;\n+                                            }\n+                                        }\n+                                        d.dismiss();\n+                                    })\n+                            .create();\n                 }\n+\n+                dialog.show();\n+                return true;\n+            case R.id.action_connect:\n+                CoreCryptoApplication.getSystem().connectAll();\n                 return true;\n             case R.id.action_sync:\n                 for (WalletManager wm: CoreCryptoApplication.getSystem().getWalletManagers()) {\n@@ -99,18 +139,39 @@ public class WalletListActivity extends AppCompatActivity implements DefaultSyst\n                 }\n                 return true;\n             case R.id.action_disconnect:\n-                for (WalletManager wm: CoreCryptoApplication.getSystem().getWalletManagers()) {\n-                    wm.disconnect();\n-                }\n+                CoreCryptoApplication.getSystem().disconnectAll();\n                 return true;\n             case R.id.action_reset:\n                 CoreCryptoApplication.resetSystem();\n                 walletsAdapter.clear();\n                 return true;\n+            case R.id.action_wipe:\n+                CoreCryptoApplication.wipeSystem();\n+                walletsAdapter.clear();\n+                return true;\n         }\n         return false;\n     }\n \n+    private List<Currency> getMissingCurrencies() {\n+        Set<Currency> missingCurrencies = new HashSet<>();\n+\n+        for (WalletManager manager: CoreCryptoApplication.getSystem().getWalletManagers()) {\n+            // get all currencies for existing wallet managers\n+            missingCurrencies.addAll(manager.getNetwork().getCurrencies());\n+\n+            for (Wallet wallet: manager.getWallets()) {\n+                // remove the currencies for existing wallets\n+                 missingCurrencies.remove(wallet.getCurrency());\n+            }\n+        }\n+\n+        // sort by currency code\n+        List<Currency> currencies = new ArrayList<>(missingCurrencies);\n+        Collections.sort(currencies, (o1, o2) -> o1.getCode().compareTo(o2.getCode()));\n+        return currencies;\n+    }\n+\n     @Override\n     public void handleWalletEvent(System system, WalletManager manager, Wallet wallet, WalletEvent event) {\n         event.accept(new DefaultWalletEventVisitor<Void>() {\ndiff --git a/Java/CryptoDemo/src/main/res/menu/menu_wallet_list.xml b/Java/CryptoDemo/src/main/res/menu/menu_wallet_list.xml\nindex 3014c396..67820994 100644\n--- a/Java/CryptoDemo/src/main/res/menu/menu_wallet_list.xml\n+++ b/Java/CryptoDemo/src/main/res/menu/menu_wallet_list.xml\n@@ -3,6 +3,11 @@\n     xmlns:app="http://schemas.android.com/apk/res-auto"\n     xmlns:tools="http://schemas.android.com/tools">\n \n+    <item android:id="@+id/action_add_wallet"\n+        android:title="Add"\n+        app:showAsAction="never"\n+        tools:ignore="HardcodedText" />\n+\n     <item android:id="@+id/action_connect"\n         android:title="Connect"\n         app:showAsAction="never"\n@@ -23,4 +28,9 @@\n         app:showAsAction="never"\n         tools:ignore="HardcodedText" />\n \n+    <item android:id="@+id/action_wipe"\n+        android:title="Wipe"\n+        app:showAsAction="never"\n+        tools:ignore="HardcodedText" />\n+\n </menu>\n\\ No newline at end of file\ndiff --git a/Swift/BRCore.xcodeproj/project.pbxproj b/Swift/BRCore.xcodeproj/project.pbxproj\nindex a9b58486..76ccf8b1 100644\n--- a/Swift/BRCore.xcodeproj/project.pbxproj\n+++ b/Swift/BRCore.xcodeproj/project.pbxproj\n@@ -60,6 +60,8 @@\n \t\t3C5E1771232D461500A558C7 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 3C5E176F232D461500A558C7 /* LICENSE */; };\n \t\t3C5E1773232D466700A558C7 /* CONTRIBUTORS in Resources */ = {isa = PBXBuildFile; fileRef = 3C5E1772232D466700A558C7 /* CONTRIBUTORS */; };\n \t\t3C5F5E75212C82310038B732 /* BREthereumMPT.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C5F5E74212C82310038B732 /* BREthereumMPT.c */; };\n+\t\t3C69A0EA233AC0FE005E7033 /* BREthereumEWMPersist.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C69A0E9233AC0FE005E7033 /* BREthereumEWMPersist.c */; };\n+\t\t3C69A0EB233AC0FE005E7033 /* BREthereumEWMPersist.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C69A0E9233AC0FE005E7033 /* BREthereumEWMPersist.c */; };\n \t\t3C6AE12B2329855300016196 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C6AE1272329855300016196 /* sqlite3.c */; settings = {COMPILER_FLAGS = "-D_HAVE_SQLITE_CONFIG_H=1 -Wno-ambiguous-macro -Wno-shorten-64-to-32 -Wno-unreachable-code -Wno-#warnings"; }; };\n \t\t3C6AE12D2329856F00016196 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C6AE1272329855300016196 /* sqlite3.c */; };\n \t\t3C6B172F2131CB5B003C313B /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C6B172E2131CB5B003C313B /* main.c */; };\n@@ -146,7 +148,7 @@\n \t\t3C902623210A7E9800143B69 /* BREthereumTransfer.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C902622210A7E9800143B69 /* BREthereumTransfer.c */; };\n \t\t3C915B0922E0B66400AAD000 /* BRCryptoHasher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C915B0822E0B66400AAD000 /* BRCryptoHasher.swift */; };\n \t\t3C915B0B22E0B6B000AAD000 /* BRCryptoCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C915B0A22E0B6B000AAD000 /* BRCryptoCoder.swift */; };\n-\t\t3C915B0D22E0B98D00AAD000 /* BRCryptoEncrypter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C915B0C22E0B98D00AAD000 /* BRCryptoEncrypter.swift */; };\n+\t\t3C915B0D22E0B98D00AAD000 /* BRCryptoCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C915B0C22E0B98D00AAD000 /* BRCryptoCipher.swift */; };\n \t\t3C915B0F22E0C67800AAD000 /* BRCryptoSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C915B0E22E0C67800AAD000 /* BRCryptoSigner.swift */; };\n \t\t3C915B1122E0EBE900AAD000 /* BRCryptoCommonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C915B1022E0EBE900AAD000 /* BRCryptoCommonTests.swift */; };\n \t\t3C97E2352241658E003FD88F /* BRCryptoCurrency.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C97E2332241658E003FD88F /* BRCryptoCurrency.c */; };\n@@ -290,6 +292,14 @@\n \t\tC3C864A7227CA28E0055120E /* BRRippleBase58.c in Sources */ = {isa = PBXBuildFile; fileRef = C3C864A6227CA28E0055120E /* BRRippleBase58.c */; };\n \t\tCA92F7902100F9CA0015C966 /* BRKeccak.c in Sources */ = {isa = PBXBuildFile; fileRef = CA92F78F2100F9CA0015C966 /* BRKeccak.c */; };\n \t\tCE0DBEB923032F1B00FC72BD /* BRSyncManager.c in Sources */ = {isa = PBXBuildFile; fileRef = CE0DBEB823032F1A00FC72BD /* BRSyncManager.c */; };\n+\t\tCE179CA7233A4FF400633B97 /* BRCryptoCipher.c in Sources */ = {isa = PBXBuildFile; fileRef = CE179CA6233A4FF400633B97 /* BRCryptoCipher.c */; };\n+\t\tCE179CA8233A4FF400633B97 /* BRCryptoCipher.c in Sources */ = {isa = PBXBuildFile; fileRef = CE179CA6233A4FF400633B97 /* BRCryptoCipher.c */; };\n+\t\tCE5E43C0233902A4001E9238 /* BRCryptoHasher.c in Sources */ = {isa = PBXBuildFile; fileRef = CE5E43BE233902A4001E9238 /* BRCryptoHasher.c */; };\n+\t\tCE5E43C1233902A4001E9238 /* BRCryptoHasher.c in Sources */ = {isa = PBXBuildFile; fileRef = CE5E43BE233902A4001E9238 /* BRCryptoHasher.c */; };\n+\t\tCE5E43C423390FBB001E9238 /* BRCryptoCoder.c in Sources */ = {isa = PBXBuildFile; fileRef = CE5E43C223390FBB001E9238 /* BRCryptoCoder.c */; };\n+\t\tCE5E43C523390FBB001E9238 /* BRCryptoCoder.c in Sources */ = {isa = PBXBuildFile; fileRef = CE5E43C223390FBB001E9238 /* BRCryptoCoder.c */; };\n+\t\tCE5E43C8233930F1001E9238 /* BRCryptoSigner.c in Sources */ = {isa = PBXBuildFile; fileRef = CE5E43C6233930F1001E9238 /* BRCryptoSigner.c */; };\n+\t\tCE5E43C9233930F1001E9238 /* BRCryptoSigner.c in Sources */ = {isa = PBXBuildFile; fileRef = CE5E43C6233930F1001E9238 /* BRCryptoSigner.c */; };\n \t\tCE6040C222BD69E40003967B /* BRWalletManagerEvent.c in Sources */ = {isa = PBXBuildFile; fileRef = CE6040C022BD69E40003967B /* BRWalletManagerEvent.c */; };\n \t\tCE6040C322BD69E40003967B /* BRWalletManagerEvent.c in Sources */ = {isa = PBXBuildFile; fileRef = CE6040C022BD69E40003967B /* BRWalletManagerEvent.c */; };\n \t\tCEE6CDE92305DDC8006724EB /* testBwm.c in Sources */ = {isa = PBXBuildFile; fileRef = CEE6CDE82305DDA6006724EB /* testBwm.c */; };\n@@ -565,6 +575,7 @@\n \t\t3C5E1804232D574A00A558C7 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; };\n \t\t3C5F5E73212C82310038B732 /* BREthereumMPT.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BREthereumMPT.h; sourceTree = "<group>"; };\n \t\t3C5F5E74212C82310038B732 /* BREthereumMPT.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BREthereumMPT.c; sourceTree = "<group>"; };\n+\t\t3C69A0E9233AC0FE005E7033 /* BREthereumEWMPersist.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BREthereumEWMPersist.c; sourceTree = "<group>"; };\n \t\t3C6AE1272329855300016196 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqlite3.c; sourceTree = "<group>"; };\n \t\t3C6AE1282329855300016196 /* shell.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = shell.c; sourceTree = "<group>"; };\n \t\t3C6AE1292329855300016196 /* sqlite3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqlite3.h; sourceTree = "<group>"; };\n@@ -589,7 +600,7 @@\n \t\t3C902622210A7E9800143B69 /* BREthereumTransfer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BREthereumTransfer.c; sourceTree = "<group>"; };\n \t\t3C915B0822E0B66400AAD000 /* BRCryptoHasher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCryptoHasher.swift; sourceTree = "<group>"; };\n \t\t3C915B0A22E0B6B000AAD000 /* BRCryptoCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCryptoCoder.swift; sourceTree = "<group>"; };\n-\t\t3C915B0C22E0B98D00AAD000 /* BRCryptoEncrypter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCryptoEncrypter.swift; sourceTree = "<group>"; };\n+\t\t3C915B0C22E0B98D00AAD000 /* BRCryptoCipher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCryptoCipher.swift; sourceTree = "<group>"; };\n \t\t3C915B0E22E0C67800AAD000 /* BRCryptoSigner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCryptoSigner.swift; sourceTree = "<group>"; };\n \t\t3C915B1022E0EBE900AAD000 /* BRCryptoCommonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCryptoCommonTests.swift; sourceTree = "<group>"; };\n \t\t3C97E2332241658E003FD88F /* BRCryptoCurrency.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BRCryptoCurrency.c; sourceTree = "<group>"; };\n@@ -724,6 +735,14 @@\n \t\tCA92F78F2100F9CA0015C966 /* BRKeccak.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BRKeccak.c; sourceTree = "<group>"; };\n \t\tCE0DBEB723032F1A00FC72BD /* BRSyncManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BRSyncManager.h; sourceTree = "<group>"; };\n \t\tCE0DBEB823032F1A00FC72BD /* BRSyncManager.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BRSyncManager.c; sourceTree = "<group>"; };\n+\t\tCE179CA5233A4FF400633B97 /* BRCryptoCipher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BRCryptoCipher.h; sourceTree = "<group>"; };\n+\t\tCE179CA6233A4FF400633B97 /* BRCryptoCipher.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BRCryptoCipher.c; sourceTree = "<group>"; };\n+\t\tCE5E43BE233902A4001E9238 /* BRCryptoHasher.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BRCryptoHasher.c; sourceTree = "<group>"; };\n+\t\tCE5E43BF233902A4001E9238 /* BRCryptoHasher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BRCryptoHasher.h; sourceTree = "<group>"; };\n+\t\tCE5E43C223390FBB001E9238 /* BRCryptoCoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BRCryptoCoder.c; sourceTree = "<group>"; };\n+\t\tCE5E43C323390FBB001E9238 /* BRCryptoCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BRCryptoCoder.h; sourceTree = "<group>"; };\n+\t\tCE5E43C6233930F1001E9238 /* BRCryptoSigner.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BRCryptoSigner.c; sourceTree = "<group>"; };\n+\t\tCE5E43C7233930F1001E9238 /* BRCryptoSigner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BRCryptoSigner.h; sourceTree = "<group>"; };\n \t\tCE6040C022BD69E40003967B /* BRWalletManagerEvent.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BRWalletManagerEvent.c; sourceTree = "<group>"; };\n \t\tCE6040C122BD69E40003967B /* BRWalletManagerPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BRWalletManagerPrivate.h; sourceTree = "<group>"; };\n \t\tCEA3F92D22CB8282006A330E /* BRBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BRBase.h; sourceTree = "<group>"; };\n@@ -1012,6 +1031,7 @@\n \t\t\t\t3C2A53A220AF595400C430F6 /* BREthereumEWM.c */,\n \t\t\t\t3C386DD220C6F6070065E355 /* BREthereumEWMEvent.c */,\n \t\t\t\t3C386DD120C6F6060065E355 /* BREthereumEWMClient.c */,\n+\t\t\t\t3C69A0E9233AC0FE005E7033 /* BREthereumEWMPersist.c */,\n \t\t\t\t3C9025F82106511700143B69 /* testEwm.c */,\n \t\t\t);\n \t\t\tpath = ewm;\n@@ -1342,8 +1362,16 @@\n \t\t\t\t3C97E24422416AB1003FD88F /* BRCryptoAmount.c */,\n \t\t\t\t3C97E254224170B9003FD88F /* BRCryptoAccount.h */,\n \t\t\t\t3C97E255224170B9003FD88F /* BRCryptoAccount.c */,\n+\t\t\t\tCE179CA6233A4FF400633B97 /* BRCryptoCipher.c */,\n+\t\t\t\tCE179CA5233A4FF400633B97 /* BRCryptoCipher.h */,\n+\t\t\t\tCE5E43C223390FBB001E9238 /* BRCryptoCoder.c */,\n+\t\t\t\tCE5E43C323390FBB001E9238 /* BRCryptoCoder.h */,\n+\t\t\t\tCE5E43BE233902A4001E9238 /* BRCryptoHasher.c */,\n+\t\t\t\tCE5E43BF233902A4001E9238 /* BRCryptoHasher.h */,\n \t\t\t\t3C1D1F2322F0F1740028B20C /* BRCryptoKey.h */,\n \t\t\t\t3C1D1F2422F0F1740028B20C /* BRCryptoKey.c */,\n+\t\t\t\tCE5E43C6233930F1001E9238 /* BRCryptoSigner.c */,\n+\t\t\t\tCE5E43C7233930F1001E9238 /* BRCryptoSigner.h */,\n \t\t\t\t3C97E258224170EC003FD88F /* BRCryptoAddress.h */,\n \t\t\t\t3C97E259224170EC003FD88F /* BRCryptoAddress.c */,\n \t\t\t\t3CCC61E9228C7FB500C0A13E /* BRCryptoFeeBasis.h */,\n@@ -1398,7 +1426,7 @@\n \t\t\t\t3CF7683322E5F6DB0007BBDA /* BRCryptoKey.swift */,\n \t\t\t\t3C915B0822E0B66400AAD000 /* BRCryptoHasher.swift */,\n \t\t\t\t3C915B0A22E0B6B000AAD000 /* BRCryptoCoder.swift */,\n-\t\t\t\t3C915B0C22E0B98D00AAD000 /* BRCryptoEncrypter.swift */,\n+\t\t\t\t3C915B0C22E0B98D00AAD000 /* BRCryptoCipher.swift */,\n \t\t\t\t3C915B0E22E0C67800AAD000 /* BRCryptoSigner.swift */,\n \t\t\t);\n \t\t\tpath = common;\n@@ -1786,7 +1814,7 @@\n \t\t\t\t3C0EAE1A2191145A000577A6 /* BRSupport.swift in Sources */,\n \t\t\t\t3CCB1349224D1B1700ADCDB9 /* BRBlockChainDB.swift in Sources */,\n \t\t\t\tCEF5E0F22315C290009EF3C0 /* BRCryptoPayment.swift in Sources */,\n-\t\t\t\t3C915B0D22E0B98D00AAD000 /* BRCryptoEncrypter.swift in Sources */,\n+\t\t\t\t3C915B0D22E0B98D00AAD000 /* BRCryptoCipher.swift in Sources */,\n \t\t\t\t3CF66B1822AAE45400C1E9BB /* BRCryptoCurrency.swift in Sources */,\n \t\t\t\t3CF66B1522AAE45400C1E9BB /* BRCryptoAccount.swift in Sources */,\n \t\t\t\t3CF66B1122AAE45400C1E9BB /* BRCryptoNetwork.swift in Sources */,\n@@ -1861,6 +1889,7 @@\n \t\t\t\t3C6B173F2131CE12003C313B /* BREthereumBloomFilter.c in Sources */,\n \t\t\t\t3C9B8DEE22BD52940060C3A9 /* BRGenericHandlers.c in Sources */,\n \t\t\t\t3C6B17402131CE12003C313B /* BREthereumNetwork.c in Sources */,\n+\t\t\t\tCE5E43C523390FBB001E9238 /* BRCryptoCoder.c in Sources */,\n \t\t\t\t3C97E2362241658E003FD88F /* BRCryptoCurrency.c in Sources */,\n \t\t\t\t3C6B17412131CE12003C313B /* BREthereumTransactionReceipt.c in Sources */,\n \t\t\t\t3C0D297A216FD0DB003838E9 /* BREthereumMessageDIS.c in Sources */,\n@@ -1883,12 +1912,14 @@\n \t\t\t\t3CD57F6D21F696C5003B0306 /* BREthereumProofOfWork.c in Sources */,\n \t\t\t\t3C6B174D2131CE12003C313B /* BREventQueue.c in Sources */,\n \t\t\t\t3C6B174E2131CE12003C313B /* BREthereumLES.c in Sources */,\n+\t\t\t\tCE5E43C9233930F1001E9238 /* BRCryptoSigner.c in Sources */,\n \t\t\t\t3C6B174F2131CE12003C313B /* BREthereumBCSEvent.c in Sources */,\n \t\t\t\t3C9B8DEC22BD52940060C3A9 /* BRGenericWalletManager.c in Sources */,\n \t\t\t\t3C97E25222416AB1003FD88F /* BRCryptoAmount.c in Sources */,\n \t\t\t\t3C7BE5A6230EFD14005FD4CD /* BRCryptoHash.c in Sources */,\n \t\t\t\t3C6B17502131CE12003C313B /* BREventAlarm.c in Sources */,\n \t\t\t\t3C6B17512131CE12003C313B /* BREthereumLESFrameCoder.c in Sources */,\n+\t\t\t\tCE179CA8233A4FF400633B97 /* BRCryptoCipher.c in Sources */,\n \t\t\t\t3CCB136B2253C84E00ADCDB9 /* BRSyncMode.c in Sources */,\n \t\t\t\t3C6B17522131CE12003C313B /* BRKeccak.c in Sources */,\n \t\t\t\t3CCD36B221A5BB8D0032637A /* BRWalletManager.c in Sources */,\n@@ -1898,6 +1929,7 @@\n \t\t\t\t3CFEFE2422BC3AF20044153D /* BRCryptoWalletManagerClient.c in Sources */,\n \t\t\t\t3C25BF482236EE40004B093F /* BRBCashParams.c in Sources */,\n \t\t\t\t3C6B17552131CE12003C313B /* BRUtilMath.c in Sources */,\n+\t\t\t\t3C69A0EB233AC0FE005E7033 /* BREthereumEWMPersist.c in Sources */,\n \t\t\t\t3C6B17562131CE12003C313B /* BRUtilMathParse.c in Sources */,\n \t\t\t\t3C6B17572131CE12003C313B /* BRUtilHex.c in Sources */,\n \t\t\t\t3C6B17582131CE12003C313B /* BRBCashAddr.c in Sources */,\n@@ -1942,6 +1974,7 @@\n \t\t\t\t3C97E257224170B9003FD88F /* BRCryptoAccount.c in Sources */,\n \t\t\t\t3C6B176E2131CE12003C313B /* BRSet.c in Sources */,\n \t\t\t\t3C9B8DF422BD55CA0060C3A9 /* BRRippleWallet.c in Sources */,\n+\t\t\t\tCE5E43C1233902A4001E9238 /* BRCryptoHasher.c in Sources */,\n \t\t\t\t3C6B176F2131CE12003C313B /* BREthereumLESRandom.c in Sources */,\n \t\t\t\t3C1D1F2622F0F1740028B20C /* BRCryptoKey.c in Sources */,\n \t\t\t\t3C6B17702131CE12003C313B /* BREthereumEWMEvent.c in Sources */,\n@@ -1986,6 +2019,7 @@\n \t\t\t\t3CAB60BE20AF8D1A00810CE4 /* BREthereumAmount.c in Sources */,\n \t\t\t\t3CAB60C020AF8D1A00810CE4 /* BREthereumAccount.c in Sources */,\n \t\t\t\t3C3DC5BB21DFCA7C004188BD /* BRFileService.c in Sources */,\n+\t\t\t\tCE5E43C423390FBB001E9238 /* BRCryptoCoder.c in Sources */,\n \t\t\t\t3CAB60C120AF8D1A00810CE4 /* BREthereumWallet.c in Sources */,\n \t\t\t\t3C7BE5AA230EFD36005FD4CD /* BRCryptoWallet.c in Sources */,\n \t\t\t\t3C386DCF20C6F5E40065E355 /* BREthereumBCS.c in Sources */,\n@@ -2016,15 +2050,18 @@\n \t\t\t\t3CF66AE722A8358200C1E9BB /* BRCryptoNetwork.c in Sources */,\n \t\t\t\t3CAB60D320AF8D1A00810CE4 /* BRUtilMathParse.c in Sources */,\n \t\t\t\t3CAB60D420AF8D1A00810CE4 /* BRUtilHex.c in Sources */,\n+\t\t\t\t3C69A0EA233AC0FE005E7033 /* BREthereumEWMPersist.c in Sources */,\n \t\t\t\t3CAB60D520AF8D1A00810CE4 /* BRBCashAddr.c in Sources */,\n \t\t\t\t3CF66AE622A8342200C1E9BB /* BRCryptoWalletManager.c in Sources */,\n \t\t\t\t3C6AE12B2329855300016196 /* sqlite3.c in Sources */,\n+\t\t\t\tCE5E43C8233930F1001E9238 /* BRCryptoSigner.c in Sources */,\n \t\t\t\t3CAB60D620AF8D1A00810CE4 /* BRAddress.c in Sources */,\n \t\t\t\t3CEF5FD42208C6E40010A811 /* BRAssert.c in Sources */,\n \t\t\t\t3CAB60D720AF8D1A00810CE4 /* BRBase58.c in Sources */,\n \t\t\t\t3C9025FD2108DDAE00143B69 /* BREthereumBCSSync.c in Sources */,\n \t\t\t\t3CAB60D820AF8D1A00810CE4 /* BRBech32.c in Sources */,\n \t\t\t\t3CAB60D920AF8D1A00810CE4 /* BRBIP32Sequence.c in Sources */,\n+\t\t\t\tCE179CA7233A4FF400633B97 /* BRCryptoCipher.c in Sources */,\n \t\t\t\t3C25BF4A2236EE71004B093F /* BRChainParams.c in Sources */,\n \t\t\t\t3C54A7FF2121F1D200C57B1B /* BREthereumMessage.c in Sources */,\n \t\t\t\t3C1D1F2522F0F1740028B20C /* BRCryptoKey.c in Sources */,\n@@ -2039,6 +2076,7 @@\n \t\t\t\t3CAB60DD20AF8D1A00810CE4 /* BRCrypto.c in Sources */,\n \t\t\t\t3C9B8DEB22BD52940060C3A9 /* BRGenericWalletManager.c in Sources */,\n \t\t\t\t3C97E25A224170EC003FD88F /* BRCryptoAddress.c in Sources */,\n+\t\t\t\tCE5E43C0233902A4001E9238 /* BRCryptoHasher.c in Sources */,\n \t\t\t\t3CAB60DE20AF8D1A00810CE4 /* BRKeyECIES.c in Sources */,\n \t\t\t\t3C97E24F22416AB1003FD88F /* BRCryptoUnit.c in Sources */,\n \t\t\t\t3CAB60DF20AF8D1A00810CE4 /* BRKey.c in Sources */,\ndiff --git a/Swift/BRCrypto/BRCryptoSystem.swift b/Swift/BRCrypto/BRCryptoSystem.swift\nindex d52c4b50..a99b5207 100644\n--- a/Swift/BRCrypto/BRCryptoSystem.swift\n+++ b/Swift/BRCrypto/BRCryptoSystem.swift\n@@ -367,6 +367,31 @@ public final class System {\n         return managers.flatMap { $0.wallets }\n     }\n \n+    static func ensurePath (_ path: String) -> Bool {\n+        // Apple on `fileExists`, `isWritableFile`\n+        //    "The following methods are of limited utility. Attempting to predicate behavior\n+        //     based on the current state of the filesystem or a particular file on the filesystem\n+        //     is encouraging odd behavior in the face of filesystem race conditions. It\'s far\n+        //     better to attempt an operation (like loading a file or creating a directory) and\n+        //     handle the error gracefully than it is to try to figure out ahead of time whether\n+        //     the operation will succeed."\n+\n+        do {\n+            // Ensure that `storagePath` exists.  Seems the return value can be ignore:\n+            //    "true if the directory was created, true if createIntermediates is set and the\n+            //     directory already exists, or false if an error occurred."\n+            // The `attributes` need not be provided as we have write permission :\n+            //    "Permissions are set according to the umask of the current process"\n+            try FileManager.default.createDirectory (atPath: path,\n+                                                     withIntermediateDirectories: true,\n+                                                     attributes: nil)\n+        }\n+        catch { return false }\n+\n+        // Ensure `path` is writeable.\n+        return FileManager.default.isWritableFile (atPath: path)\n+    }\n+\n     ///\n     /// Initialize System\n     ///\n@@ -386,16 +411,19 @@ public final class System {\n     ///   - listenerQueue: The queue to use when performing listen event handler callbacks.  If a\n     ///       queue is not specficied (default to `nil`), then one will be provided.\n     ///\n-    public init (listener: SystemListener,\n-                 account: Account,\n-                 onMainnet: Bool,\n-                 path: String,\n-                 query: BlockChainDB,\n-                 listenerQueue: DispatchQueue? = nil) {\n+    internal init (listener: SystemListener,\n+                  account: Account,\n+                  onMainnet: Bool,\n+                  path: String,\n+                  query: BlockChainDB,\n+                  listenerQueue: DispatchQueue? = nil) {\n+        let accounctSpecificPath = path + (path.last == "/" ? "" : "/") + account.fileSystemIdentifier\n+        precondition (System.ensurePath(accounctSpecificPath))\n+\n         self.listener  = listener\n         self.account   = account\n         self.onMainnet = onMainnet\n-        self.path  = path + (path.last == "/" ? "" : "/") + account.fileSystemIdentifier\n+        self.path  = accounctSpecificPath\n         self.query = query\n         self.listenerQueue = listenerQueue ?? DispatchQueue (label: "Crypto System Listener")\n         self.callbackCoordinator = SystemCallbackCoordinator (queue: self.listenerQueue)\n@@ -514,12 +542,19 @@ public final class System {\n     }\n \n     ///\n-    /// Stop the system.  All managers are disconnected.  Will inhibit `System` processing.\n+    /// Disconnect all wallet managers\n     ///\n-    public func stop () {\n+    public func disconnectAll () {\n         managers.forEach { $0.disconnect() }\n     }\n \n+    ///\n+    /// Connect all wallet managers.  They will be connected w/o an explict NetworkPeer.\n+    ///\n+    public func connectAll () {\n+        managers.forEach { $0.connect() }\n+    }\n+\n     ///\n     /// Set the network reachable flag for all managers. Setting or clearing this flag will\n     /// NOT result in a connect/disconnect operation by a manager. Callers must use the\n@@ -792,8 +827,8 @@ public final class System {\n     /// A index to globally identify systems.\n     static var systemIndex: Int32 = 0;\n \n-    /// A dictionary mapping an index to a system weakly.\n-    static var systemMapping: [Int32 : Weak<System>] = [:]\n+    /// A dictionary mapping an index to a system.\n+    static var systemMapping: [Int32 : System] = [:]\n \n     ///\n     /// Lookup a `System` from an `index\n@@ -804,7 +839,26 @@ public final class System {\n     ///\n     static func systemLookup (index: Int32) -> System? {\n         return systemQueue.sync {\n-            return systemMapping[index]?.value\n+            return systemMapping[index]\n+        }\n+    }\n+\n+    /// An array of removed systems.  This is a workaround for systems that have been destroyed.\n+    /// We do not correctly handle \'release\' and thus C-level memory issues are introduced; rather\n+    /// than solving those memory issues now, we\'ll avoid \'release\' by holding a reference.\n+    private static var systemRemovedSystems = [System]()\n+\n+    /// If true, save removed system in the above array. Set to `false` for debugging \'release\'.\n+    private static var systemRemovedSystemsSave = true;\n+\n+    static func systemRemove (index: Int32) {\n+        return systemQueue.sync {\n+            systemMapping.removeValue(forKey: index)\n+                .map {\n+                    if systemRemovedSystemsSave {\n+                        systemRemovedSystems.append ($0)\n+                    }\n+            }\n         }\n     }\n \n@@ -819,7 +873,7 @@ public final class System {\n         systemQueue.async (flags: .barrier) {\n             systemIndex += 1\n             system.index = systemIndex // Always 1 or more\n-            systemMapping[system.index] = Weak (value: system)\n+            systemMapping[system.index] = system\n         }\n     }\n \n@@ -871,6 +925,84 @@ public final class System {\n         let index = Int(self.index)\n         return UnsafeMutableRawPointer (bitPattern: index)\n     }\n+\n+    public static func create (listener: SystemListener,\n+                               account: Account,\n+                               onMainnet: Bool,\n+                               path: String,\n+                               query: BlockChainDB,\n+                               listenerQueue: DispatchQueue? = nil) -> System {\n+        return System (listener: listener,\n+                       account: account,\n+                       onMainnet: onMainnet,\n+                       path: path,\n+                       query: query,\n+                       listenerQueue: listenerQueue)\n+    }\n+\n+    static func destroy (system: System) {\n+        // Stop all callbacks.  This might be inconsistent with \'deleted\' events.\n+        System.systemRemove (index: system.index)\n+\n+        // Disconnect all wallet managers\n+        system.disconnectAll()\n+\n+        // Stop all the wallet managers.\n+        system.managers.forEach { $0.stop() }\n+    }\n+\n+    ///\n+    /// Cease use of `system` and remove (aka \'wipe\') its persistent storage.  Caution is highly\n+    /// warranted; none of the System\'s references, be they Wallet Managers, Wallets, Transfers, etc\n+    /// should be *touched* once the system is wiped.\n+    ///\n+    /// - Note: This function blocks until completed.  Be sure that all references are dereferenced\n+    ///         *before* invoking this function and remove the reference to `system` after this\n+    ///         returns.\n+    ///\n+    /// - Parameter system: the system to wipe\n+    ///\n+    public static func wipe (system: System) {\n+        // Save the path to the persistent storage\n+        let storagePath = system.path;\n+\n+        // Destroy the system.\n+        destroy (system: system)\n+\n+        // Clear out persistent storage\n+        do {\n+            if FileManager.default.fileExists(atPath: storagePath) {\n+                try FileManager.default.removeItem(atPath: storagePath)\n+            }\n+        }\n+        catch let error as NSError {\n+            print("Error: \\(error.localizedDescription)")\n+        }\n+    }\n+\n+    ///\n+    /// Remove (aka \'wipe\') the persistent storage associated with any and all systems located\n+    /// within `atPath` except for a specified array of systems to preserve.  Generally, this\n+    /// function should be called on startup after all systems have been created.  When called at\n+    ///  that time, any \'left over\' systems will have their persistent storeage wiped.\n+    ///\n+    /// - Parameter atPath: the file system path where system data is persistently stored\n+    /// - Parameter systems: the array of systems that shouldn not have their data wiped.\n+    ///\n+    public static func wipeAll (atPath: String, except systems: [System]) {\n+        do {\n+            try FileManager.default.contentsOfDirectory (atPath: atPath)\n+                .map     { (path) in atPath + "/" + path }\n+                .filter  { (path) in !systems.contains { path == $0.path } }\n+                .forEach { (path) in\n+                    do {\n+                        try FileManager.default.removeItem (atPath: path)\n+                    }\n+                    catch {}\n+            }\n+        }\n+        catch {}\n+    }\n }\n \n public enum SystemEvent {\n@@ -1507,7 +1639,14 @@ extension System {\n                 manager.query.getBlockNumberAsETH (network: network) {\n                     (res: Result<String, BlockChainDB.QueryError>) in\n                     defer { cryptoWalletManagerGive (cwm!) }\n-                    res.resolve (\n+                    // If we get a successful response, but the provided blocknumber is "0" then\n+                    // that indicates that the JSON-RPC node is syncing.  Thus, if "0" transform\n+                    // to a .failure\n+                    res.flatMap {\n+                        return ($0 != "0" && $0 != "0x0"\n+                            ? Result.success ($0)\n+                            : Result.failure (BlockChainDB.QueryError.noData))\n+                    }.resolve (\n                         success: { cwmAnnounceGetBlockNumberSuccessAsString (cwm, sid, $0) },\n                         failure: { (_) in cwmAnnounceGetBlockNumberFailure (cwm, sid) })\n                 }},\ndiff --git a/Swift/BRCrypto/BRCryptoWalletManager.swift b/Swift/BRCrypto/BRCryptoWalletManager.swift\nindex 4bd54b63..d576fd08 100644\n--- a/Swift/BRCrypto/BRCryptoWalletManager.swift\n+++ b/Swift/BRCrypto/BRCryptoWalletManager.swift\n@@ -181,6 +181,10 @@ public final class WalletManager: Equatable, CustomStringConvertible {\n         cryptoWalletManagerDisconnect (core)\n     }\n \n+    internal func stop () {\n+        cryptoWalletManagerStop (core);\n+    }\n+    \n     public func sync () {\n         cryptoWalletManagerSync (core)\n     }\ndiff --git a/Swift/BRCrypto/common/BRCryptoCipher.swift b/Swift/BRCrypto/common/BRCryptoCipher.swift\nnew file mode 100644\nindex 00000000..3b16ade6\n--- /dev/null\n+++ b/Swift/BRCrypto/common/BRCryptoCipher.swift\n@@ -0,0 +1,100 @@\n+//\n+//  BRCryptoEncryptor.swift\n+//  BRCrypto\n+//\n+//  Created by Ed Gamble on 7/18/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+//\n+\n+import Foundation\n+import BRCryptoC\n+\n+public protocol Cipher {\n+    func encrypt (data: Data) -> Data?\n+    func decrypt (data: Data) -> Data?\n+}\n+\n+public final class CoreCipher: Cipher {\n+    public static func aes_ecb(key: Data) -> CoreCipher {\n+        return key.withUnsafeBytes { (keyBytes: UnsafeRawBufferPointer) -> CoreCipher in\n+            let keyAddr = keyBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+            let core = cryptoCipherCreateForAESECB(keyAddr, keyBytes.count)!\n+            return CoreCipher (core: core)\n+        }\n+    }\n+\n+    public static func chacha20_poly1305(key: Key, nonce12: Data, ad: Data) -> CoreCipher {\n+        return nonce12.withUnsafeBytes { (nonce12Bytes: UnsafeRawBufferPointer) -> CoreCipher in\n+            let nonce12Addr  = nonce12Bytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+            return ad.withUnsafeBytes { (adBytes: UnsafeRawBufferPointer) -> CoreCipher in\n+                let adAddr  = adBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+                let core = cryptoCipherCreateForChacha20Poly1305(key.core,\n+                                                                 nonce12Addr, nonce12Bytes.count,\n+                                                                 adAddr, adBytes.count)!\n+                return CoreCipher (core: core)\n+            }\n+        }\n+    }\n+\n+    public static func pigeon(privKey: Key, pubKey: Key, nonce12: Data) -> CoreCipher {\n+        return nonce12.withUnsafeBytes { (nonce12Bytes: UnsafeRawBufferPointer) -> CoreCipher in\n+            let nonce12Addr  = nonce12Bytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+            let core = cryptoCipherCreateForPigeon(privKey.core,\n+                                                   pubKey.core,\n+                                                   nonce12Addr, nonce12Bytes.count)!\n+            return CoreCipher (core: core)\n+        }\n+    }\n+\n+    // The Core representation\n+    internal let core: BRCryptoCipher\n+\n+    deinit { cryptoCipherGive (core) }\n+\n+    internal init (core: BRCryptoCipher) {\n+        self.core = core\n+    }\n+\n+    public func encrypt (data source: Data) -> Data? {\n+        return source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> Data? in\n+            let sourceAddr  = sourceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+            let sourceCount = sourceBytes.count\n+\n+            let targetCount = cryptoCipherEncryptLength(self.core, sourceAddr, sourceCount)\n+            guard targetCount != 0 else { return nil }\n+\n+            var result = CRYPTO_FALSE\n+            var target = Data (count: targetCount)\n+            target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n+                let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+\n+                result = cryptoCipherEncrypt (self.core, targetAddr, targetCount, sourceAddr, sourceCount)\n+            }\n+\n+            return result == CRYPTO_TRUE ? target : nil\n+        }\n+    }\n+\n+    public func decrypt (data source: Data) -> Data? {\n+        return source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> Data? in\n+            let sourceAddr  = sourceBytes.baseAddress?.assumingMemoryBound(to: Int8.self)\n+            let sourceCount = sourceBytes.count\n+\n+            let targetCount = cryptoCipherDecryptLength(self.core, sourceAddr, sourceCount)\n+            guard targetCount != 0 else { return nil }\n+\n+            var result = CRYPTO_FALSE\n+            var target = Data (count: targetCount)\n+            target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n+                let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+\n+                result = cryptoCipherDecrypt (self.core, targetAddr, targetCount, sourceAddr, sourceCount)\n+            }\n+\n+            return result == CRYPTO_TRUE ? target : nil\n+        }\n+    }\n+}\ndiff --git a/Swift/BRCrypto/common/BRCryptoCoder.swift b/Swift/BRCrypto/common/BRCryptoCoder.swift\nindex 1cfc80b0..25d35100 100644\n--- a/Swift/BRCrypto/common/BRCryptoCoder.swift\n+++ b/Swift/BRCrypto/common/BRCryptoCoder.swift\n@@ -13,87 +13,66 @@ import Foundation\n import BRCryptoC\n \n public protocol Coder {\n-    func encode (data: Data) -> String\n+    func encode (data: Data) -> String?\n     func decode (string: String) -> Data?\n }\n \n-public enum CoreCoder: Coder {\n-    case hex\n-    case base58\n-    case base58check\n+public final class CoreCoder: Coder {\n \n-    public func encode (data source: Data) -> String {\n-        let sourceCount = source.count\n-        return source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> String in\n+    public static var hex: CoreCoder {\n+        return CoreCoder (core: cryptoCoderCreate (CRYPTO_CODER_HEX)!)\n+    }\n+\n+    public static var base58: CoreCoder {\n+        return CoreCoder (core: cryptoCoderCreate (CRYPTO_CODER_BASE58)!)\n+    }\n+\n+    public static var base58check: CoreCoder {\n+        return CoreCoder (core: cryptoCoderCreate (CRYPTO_CODER_BASE58CHECK)!)\n+    }\n+\n+    // The Core representation\n+    internal let core: BRCryptoCoder\n+\n+    deinit { cryptoCoderGive (core) }\n+\n+    internal init (core: BRCryptoCoder) {\n+        self.core = core\n+    }\n+\n+    public func encode (data source: Data) -> String? {\n+        return source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> String? in\n             let sourceAddr  = sourceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+            let sourceCount = sourceBytes.count\n \n-            var target: Data!\n-            var result: String!\n-            switch self {\n-            case .hex:\n-                let targetCount = 2 * sourceCount + 1\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: Int8.self)\n-                    encodeHex (targetAddr, targetCount, sourceAddr, sourceCount)\n-                    result = String (cString: targetAddr!)\n-                }\n-\n-            case .base58:\n-                let targetCount = BRBase58Encode(nil, 0, sourceAddr, sourceCount)\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: Int8.self)\n-                    BRBase58Encode (targetAddr, targetCount, sourceAddr, sourceCount)\n-                    result = String (cString: targetAddr!)\n-               }\n-\n-            case .base58check:\n-                let targetCount = BRBase58CheckEncode (nil, 0, sourceAddr, sourceCount)\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: Int8.self)\n-                    BRBase58CheckEncode (targetAddr, targetCount, sourceAddr, sourceCount)\n-                    result = String (cString: targetAddr!)\n-               }\n-            }\n+            let targetCount = cryptoCoderEncodeLength(self.core, sourceAddr, sourceCount)\n+            guard targetCount != 0 else { return nil }\n+\n+            var target = Data (count: targetCount)\n+            return target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> String? in\n+                let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: Int8.self)\n \n-            return result\n+                let result = cryptoCoderEncode (self.core, targetAddr, targetCount, sourceAddr, sourceCount)\n+                \n+                return result == CRYPTO_TRUE ? String (cString: targetAddr!) : nil\n+            }\n         }\n     }\n \n     public func decode (string source: String) -> Data? {\n-        let sourceCount: Int = source.count\n         return source.withCString { (sourceAddr: UnsafePointer<Int8>) -> Data? in\n-            var target: Data!\n-            switch self {\n-            case .hex:\n-                let targetCount = sourceCount / 2\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    decodeHex (targetAddr, targetCount, sourceAddr, sourceCount)\n-                }\n-\n-            case .base58:\n-                let targetCount = BRBase58Decode(nil, 0, sourceAddr)\n-                if 0 == targetCount { return nil }\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    BRBase58Decode (targetAddr, targetCount, sourceAddr)\n-                }\n-\n-            case .base58check:\n-                let targetCount = BRBase58CheckDecode (nil, 0, sourceAddr)\n-                if 0 == targetCount { return nil }\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    BRBase58CheckDecode (targetAddr, targetCount, sourceAddr)\n-                }\n+            let targetCount = cryptoCoderDecodeLength(self.core, sourceAddr)\n+            guard targetCount != 0 else { return nil }\n+\n+            var result = CRYPTO_FALSE\n+            var target = Data (count: targetCount)\n+            target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n+                let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+\n+                result = cryptoCoderDecode (self.core, targetAddr, targetCount, sourceAddr)\n             }\n-            return target\n+\n+            return result == CRYPTO_TRUE ? target : nil\n         }\n     }\n }\ndiff --git a/Swift/BRCrypto/common/BRCryptoEncrypter.swift b/Swift/BRCrypto/common/BRCryptoEncrypter.swift\ndeleted file mode 100644\nindex 49bc2665..00000000\n--- a/Swift/BRCrypto/common/BRCryptoEncrypter.swift\n+++ /dev/null\n@@ -1,177 +0,0 @@\n-//\n-//  BRCryptoEncryptor.swift\n-//  BRCrypto\n-//\n-//  Created by Ed Gamble on 7/18/19.\n-//  Copyright © 2019 Breadwinner AG. All rights reserved.\n-//\n-//  See the LICENSE file at the project root for license information.\n-//  See the CONTRIBUTORS file at the project root for a list of contributors.\n-//\n-\n-import Foundation\n-import BRCryptoC\n-\n-public protocol Encrypter {\n-    func encrypt (data: Data) -> Data\n-    func decrypt (data: Data) -> Data\n-}\n-\n-public enum CoreEncrypter: Encrypter {\n-    case aes_ecb (key:Data) // count = 16, 24,or 32\n-    case chacha20_poly1305 (key:Key, nonce12:Data, ad:Data)\n-    case pigeon (privKey:Key, pubKey:Key, nonce12:Data)\n-\n-    public func encrypt (data source: Data) -> Data {\n-        var target: Data!\n-        switch self {\n-        case let .aes_ecb (key):\n-            let keyCount = key.count\n-            key.withUnsafeBytes { (keyBytes: UnsafeRawBufferPointer) -> Void in\n-                let keyAddr  = keyBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-\n-                target = Data (source)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    BRAESECBEncrypt(targetAddr, keyAddr, keyCount)\n-                }\n-            }\n-\n-        case let .chacha20_poly1305 (key, nonce12, ad):\n-            let sourceCount = source.count\n-             source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> Void in\n-                let sourceAddr  = sourceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                var secret = key.secret\n-                UnsafeMutablePointer (mutating: &secret)\n-                    .withMemoryRebound(to: UInt8.self, capacity: 32) { (secretBytes: UnsafeMutablePointer<UInt8>) -> Void in\n-                    nonce12.withUnsafeBytes { (nonceBytes: UnsafeRawBufferPointer) -> Void in\n-                        ad.withUnsafeBytes { (adBytes: UnsafeRawBufferPointer) -> Void in\n-                            let adCount = ad.count\n-                            let adAddr  = adBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-\n-                            let targetCount =\n-                                BRChacha20Poly1305AEADEncrypt (nil, 0,\n-                                                               secretBytes,\n-                                                               nonceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),\n-                                                               sourceAddr, sourceCount,\n-                                                               adAddr, adCount)\n-\n-                            target = Data (count: targetCount)\n-                            target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                                let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                                BRChacha20Poly1305AEADEncrypt (targetAddr, targetCount,\n-                                                               secretBytes,\n-                                                               nonceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),\n-                                                               sourceAddr, sourceCount,\n-                                                               adAddr, adCount)\n-                            }\n-                        }\n-                    }\n-                }\n-                secret = UInt256() // Zero\n-            }\n-\n-        case let .pigeon (privKey, pubKey, nonce12):\n-            let sourceCount = source.count\n-            source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> Void in\n-                let sourceAddr  = sourceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-\n-                nonce12.withUnsafeBytes { (nonceBytes: UnsafeRawBufferPointer) -> Void in\n-                    let targetCount =\n-                        BRKeyPigeonEncrypt (nil, nil, 0, nil,\n-                                            nonceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),\n-                                            sourceAddr, sourceCount)\n-\n-                    target = Data (count: targetCount)\n-                    target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                        let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                        BRKeyPigeonEncrypt (cryptoKeyGetCore(privKey.core),\n-                                            targetAddr, targetCount,\n-                                            cryptoKeyGetCore(pubKey.core),\n-                                            nonceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),\n-                                            sourceAddr, sourceCount)\n-                    }\n-                }\n-            }\n-        }\n-        return target\n-    }\n-\n-    public func decrypt (data source: Data) -> Data {\n-        var target: Data!\n-        switch self {\n-        case let .aes_ecb (key):\n-            let keyCount = key.count\n-            key.withUnsafeBytes { (keyBytes: UnsafeRawBufferPointer) -> Void in\n-                let keyAddr  = keyBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-\n-                target = Data (source)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    BRAESECBDecrypt(targetAddr, keyAddr, keyCount)\n-                }\n-            }\n-\n-        case let .chacha20_poly1305 (key, nonce12, ad):\n-            let sourceCount = source.count\n-            source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> Void in\n-                let sourceAddr  = sourceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-\n-                var secret = key.secret\n-                UnsafeMutablePointer (mutating: &secret)\n-                    .withMemoryRebound(to: UInt8.self, capacity: 32) { (secretBytes: UnsafeMutablePointer<UInt8>) -> Void in\n-                    nonce12.withUnsafeBytes { (nonceBytes: UnsafeRawBufferPointer) -> Void in\n-                        ad.withUnsafeBytes { (adBytes: UnsafeRawBufferPointer) -> Void in\n-                            let adCount = ad.count\n-                            let adAddr  = adBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-\n-                            let targetCount =\n-                                BRChacha20Poly1305AEADDecrypt (nil, 0,\n-                                                               secretBytes,\n-                                                               nonceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),\n-                                                               sourceAddr, sourceCount,\n-                                                               adAddr, adCount)\n-\n-                            target = Data (count: targetCount)\n-                            target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                                let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                                BRChacha20Poly1305AEADDecrypt (targetAddr, targetCount,\n-                                                               secretBytes,\n-                                                               nonceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),\n-                                                               sourceAddr, sourceCount,\n-                                                               adAddr, adCount)\n-                            }\n-                        }\n-                    }\n-                }\n-                secret = UInt256() // Zero\n-            }\n-\n-        case let .pigeon (privKey, pubKey, nonce12):\n-            let sourceCount = source.count\n-            source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> Void in\n-                let sourceAddr  = sourceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-\n-                nonce12.withUnsafeBytes { (nonceBytes: UnsafeRawBufferPointer) -> Void in\n-                    let targetCount =\n-                        BRKeyPigeonDecrypt (nil, nil, 0, nil,\n-                                            nonceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),\n-                                            sourceAddr, sourceCount)\n-\n-                    target = Data (count: targetCount)\n-                    target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                        let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                        BRKeyPigeonDecrypt (cryptoKeyGetCore(privKey.core),\n-                                            targetAddr, targetCount,\n-                                            cryptoKeyGetCore(pubKey.core),\n-                                            nonceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),\n-                                            sourceAddr, sourceCount)\n-                    }\n-                }\n-            }\n-        }\n-\n-        return target\n-\n-    }\n-}\ndiff --git a/Swift/BRCrypto/common/BRCryptoHasher.swift b/Swift/BRCrypto/common/BRCryptoHasher.swift\nindex 58a57ec5..cf18b2d7 100644\n--- a/Swift/BRCrypto/common/BRCryptoHasher.swift\n+++ b/Swift/BRCrypto/common/BRCryptoHasher.swift\n@@ -13,113 +13,81 @@ import Foundation // for Data\n import BRCryptoC\n \n public protocol HasherX {\n-    func hash (data: Data) -> Data\n+    func hash (data: Data) -> Data?\n }\n \n+public final class CoreHasher: HasherX {\n \n-public enum CoreHasher: HasherX {\n-    case sha1\n-    case sha256\n-    case sha224\n-    case sha256_2\n-    case sha384\n-    case sha512\n-    case rmd160\n-    case hash160\n-    case sha3\n-    case keccak256\n-    case md5\n-    //    case murmur3 (seed: UInt32)\n-    //    case sip64 (key16: Data)\n-    // ...\n-\n-    public func hash (data source: Data) -> Data {\n-        return source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> Data in\n-            let sourceAddr = sourceBytes.baseAddress\n-            let sourceCount = source.count\n-\n-            var target: Data!\n-            switch self {\n-\n-            case .sha1:\n-                target = Data (count: 20)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRSHA1 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .sha256:\n-                target = Data (count: 32)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRSHA256 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .sha224:\n-                target = Data (count: 28)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRSHA224 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .sha256_2:\n-                target = Data (count: 32)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRSHA256_2 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .sha384:\n-                target = Data (count: 48)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRSHA384 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .sha512:\n-                target = Data (count: 64)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRSHA512 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .rmd160:\n-                target = Data (count: 20)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRRMD160 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .hash160:\n-                target = Data (count: 20)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRHash160 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .sha3:\n-                target = Data (count: 32)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRSHA3_256 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-          case .keccak256:\n-                target = Data (count: 32)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRKeccak256 (targetAddr, sourceAddr, sourceCount)\n-                }\n-\n-            case .md5:\n-                target = Data (count: 16)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr = targetBytes.baseAddress\n-                    BRMD5 (targetAddr, sourceAddr, sourceCount)\n-                }\n+    public static var sha1: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_SHA1)!)\n+    }\n+\n+    public static var sha224: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_SHA224)!)\n+    }\n+\n+    public static var sha256: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_SHA256)!)\n+    }\n+\n+    public static var sha256_2: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_SHA256_2)!)\n+    }\n+\n+    public static var sha384: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_SHA384)!)\n+    }\n+\n+    public static var sha512: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_SHA512)!)\n+    }\n+\n+    public static var sha3: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_SHA3)!)\n+    }\n+\n+    public static var rmd160: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_RMD160)!)\n+    }\n+\n+    public static var hash160: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_HASH160)!)\n+    }\n+\n+    public static var keccak256: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_KECCAK256)!)\n+    }\n+\n+    public static var md5: CoreHasher {\n+        return CoreHasher (core: cryptoHasherCreate (CRYPTO_HASHER_MD5)!)\n+    }\n+\n+    // The Core representation\n+    internal let core: BRCryptoHasher\n+\n+    deinit { cryptoHasherGive (core) }\n+\n+    internal init (core: BRCryptoHasher) {\n+        self.core = core\n+    }\n+\n+    public func hash (data source: Data) -> Data? {\n+        return source.withUnsafeBytes { (sourceBytes: UnsafeRawBufferPointer) -> Data? in\n+            let sourceAddr  = sourceBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+            let sourceCount = sourceBytes.count\n+\n+            let targetCount = cryptoHasherLength(self.core)\n+            guard targetCount != 0 else { return nil }\n+\n+            var result = CRYPTO_FALSE\n+            var target = Data (count: targetCount)\n+            target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n+                let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+\n+                result = cryptoHasherHash (self.core, targetAddr, targetCount, sourceAddr, sourceCount)\n             }\n \n-            return target\n+            return result == CRYPTO_TRUE ? target : nil\n         }\n     }\n  }\ndiff --git a/Swift/BRCrypto/common/BRCryptoSigner.swift b/Swift/BRCrypto/common/BRCryptoSigner.swift\nindex 5f65fa41..7411f5c4 100644\n--- a/Swift/BRCrypto/common/BRCryptoSigner.swift\n+++ b/Swift/BRCrypto/common/BRCryptoSigner.swift\n@@ -26,7 +26,7 @@ public protocol Signer {\n     ///\n     /// - Returns: the signature\n     ///\n-    func sign (data32: Data, using: Key) -> Data\n+    func sign (data32: Data, using: Key) -> Data?\n \n     ///\n     /// Recover the CryptoKey (only the public key portion) from the signed data and the signature.\n@@ -42,94 +42,73 @@ public protocol Signer {\n     func recover (data32: Data, signature: Data) -> Key?\n }\n \n-public enum CoreSigner: Signer {\n-    // Does not support \'recovery\'\n-    case basicDER\n-    case basicJOSE\n+public final class CoreSigner: Signer {\n+\n+    /// Does not support \'recovery\'\n+    public static var basicDER: CoreSigner {\n+        return CoreSigner (core: cryptoSignerCreate (CRYPTO_SIGNER_BASIC_DER)!)\n+    }\n+\n+    /// Does not support \'recovery\'\n+    public static var basicJOSE: CoreSigner {\n+        return CoreSigner (core: cryptoSignerCreate (CRYPTO_SIGNER_BASIC_JOSE)!)\n+    }\n \n     /// Does support \'recovery\'\n-    case compact\n+    public static var compact: CoreSigner {\n+        return CoreSigner (core: cryptoSignerCreate (CRYPTO_SIGNER_COMPACT)!)\n+    }\n+\n+    // The Core representation\n+    internal let core: BRCryptoSigner\n \n-    public func sign (data32 digest: Data, using privateKey: Key) -> Data {\n+    deinit { cryptoSignerGive (core) }\n+\n+    internal init (core: BRCryptoSigner) {\n+        self.core = core\n+    }\n+\n+    public func sign (data32 digest: Data, using privateKey: Key) -> Data? {\n         // Copy the key - prep to pass to Core C functions\n         let key = privateKey.core\n \n-        // Confirm `data32 digest` is 32 bytes\n-        let sourceCount = digest.count\n-        precondition (32 == sourceCount)\n-\n-        return digest.withUnsafeBytes { (digestBytes: UnsafeRawBufferPointer) -> Data in\n+        return digest.withUnsafeBytes { (digestBytes: UnsafeRawBufferPointer) -> Data? in\n             let digestAddr  = digestBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-            let digestUInt256 = digestAsUInt256 (digestAddr!)\n-\n-            var target: Data!\n-            switch self {\n-            case .basicDER:\n-                let targetCount = BRKeySign (cryptoKeyGetCore(key), nil, 0, digestUInt256)\n-                assert(targetCount > 0)\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    BRKeySign (cryptoKeyGetCore(key), targetAddr, targetCount, digestUInt256)\n-                }\n-                \n-            case .basicJOSE:\n-                let targetCount = BRKeySignJOSE (cryptoKeyGetCore(key), nil, 0, digestUInt256)\n-                assert(targetCount > 0)\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    BRKeySignJOSE (cryptoKeyGetCore(key), targetAddr, targetCount, digestUInt256)\n-                }\n-\n-            case .compact:\n-                let targetCount = BRKeyCompactSign (cryptoKeyGetCore(key), nil, 0, digestUInt256)\n-                assert(targetCount > 0)\n-                target = Data (count: targetCount)\n-                target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n-                    let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    BRKeyCompactSign (cryptoKeyGetCore(key), targetAddr, targetCount, digestUInt256)\n-                }\n+            let digestCount = digestBytes.count\n+\n+            // Confirm `data32 digest` is 32 bytes\n+            precondition (32 == digestCount)\n+\n+            let targetCount = cryptoSignerSignLength(self.core, key, digestAddr, digestCount)\n+            guard targetCount != 0 else { return nil }\n+\n+            var result = CRYPTO_FALSE\n+            var target = Data (count: targetCount)\n+            target.withUnsafeMutableBytes { (targetBytes: UnsafeMutableRawBufferPointer) -> Void in\n+                let targetAddr  = targetBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+\n+                result = cryptoSignerSign(self.core, key, targetAddr, targetCount, digestAddr, digestCount)\n             }\n \n-            return target\n+            return result == CRYPTO_TRUE ? target : nil\n         }\n     }\n \n     public func recover (data32 digest: Data, signature: Data) -> Key? {\n-        let sourceCount = digest.count\n-        precondition (32 == sourceCount)\n-\n         return digest.withUnsafeBytes { (digestBytes: UnsafeRawBufferPointer) -> Key? in\n             let digestAddr  = digestBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-            let digestUInt256 = digestAsUInt256 (digestAddr!) // : UInt256 = createUInt256(0)\n-\n-            switch self {\n-            case .basicDER, .basicJOSE:\n-                return nil\n-\n-            case .compact:\n-                let signatureCount = signature.count\n-                return signature.withUnsafeBytes { (signatureBytes: UnsafeRawBufferPointer) -> Key? in\n-                    var key = BRKey.self.init()\n-                    let signatureAddr  = signatureBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n-                    let success: Bool = 1 == BRKeyRecoverPubKey (&key, digestUInt256, signatureAddr, signatureCount)\n-                    return success\n-                        ? Key (core: cryptoKeyCreateFromKey(&key))\n-                        : nil\n-                }\n-            }\n-        }\n-    }\n+            let digestCount = digestBytes.count\n+\n+            // Confirm `data32 digest` is 32 bytes\n+            precondition (32 == digestCount)\n \n-    private func digestAsUInt256 (_ dataAddr: UnsafePointer<UInt8>) -> UInt256 {\n-        var digest: UInt256 = UInt256.self.init()\n-        return UnsafeMutablePointer(mutating: &digest)\n-            .withMemoryRebound(to: UInt8.self, capacity: 32) { (digestBytes: UnsafeMutablePointer<UInt8>) -> UInt256 in\n-                for index in 0..<32 {\n-                    digestBytes[index] = dataAddr[index]\n-                }\n-                return digest\n+            let signatureCount = signature.count\n+            return signature.withUnsafeBytes { (signatureBytes: UnsafeRawBufferPointer) -> Key? in\n+                let signatureAddr  = signatureBytes.baseAddress?.assumingMemoryBound(to: UInt8.self)\n+\n+                return cryptoSignerRecover (self.core, digestAddr, digestCount, signatureAddr, signatureCount)\n+                    .map { Key (core: $0) }\n+            }\n         }\n     }\n }\ndiff --git a/Swift/BRCryptoDemo/Base.lproj/Main.storyboard b/Swift/BRCryptoDemo/Base.lproj/Main.storyboard\nindex 2cd79c75..e51af99b 100644\n--- a/Swift/BRCryptoDemo/Base.lproj/Main.storyboard\n+++ b/Swift/BRCryptoDemo/Base.lproj/Main.storyboard\n@@ -1,11 +1,9 @@\n <?xml version="1.0" encoding="UTF-8"?>\n-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="H1p-Uh-vWS">\n-    <device id="retina4_7" orientation="portrait">\n-        <adaptation id="fullscreen"/>\n-    </device>\n+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="H1p-Uh-vWS">\n+    <device id="retina4_7" orientation="portrait" appearance="light"/>\n     <dependencies>\n         <deployment identifier="iOS"/>\n-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>\n+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>\n         <capability name="Safe area layout guides" minToolsVersion="9.0"/>\n         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>\n     </dependencies>\n@@ -20,14 +18,14 @@\n                         <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>\n                         <prototypes>\n                             <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="WalletManagerCell" textLabel="9S5-cR-ZmV" style="IBUITableViewCellStyleDefault" id="eEw-HE-2rc">\n-                                <rect key="frame" x="0.0" y="28" width="375" height="44"/>\n+                                <rect key="frame" x="0.0" y="28" width="375" height="43.5"/>\n                                 <autoresizingMask key="autoresizingMask"/>\n                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="eEw-HE-2rc" id="fS9-iS-Ovv">\n-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>\n+                                    <rect key="frame" x="0.0" y="0.0" width="347.5" height="43.5"/>\n                                     <autoresizingMask key="autoresizingMask"/>\n                                     <subviews>\n                                         <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9S5-cR-ZmV">\n-                                            <rect key="frame" x="16" y="0.0" width="324" height="43.5"/>\n+                                            <rect key="frame" x="16" y="0.0" width="323.5" height="43.5"/>\n                                             <autoresizingMask key="autoresizingMask"/>\n                                             <fontDescription key="fontDescription" type="system" pointSize="17"/>\n                                             <nil key="textColor"/>\n@@ -56,7 +54,7 @@\n             <objects>\n                 <navigationController id="7SR-fA-wIY" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="OT5-yI-1AY">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\n@@ -72,7 +70,7 @@\n             <objects>\n                 <navigationController title="Summary Navigator (Wallets)" id="RMx-3f-FxP" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="Pmd-2v-anx">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\n@@ -92,7 +90,7 @@\n                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>\n                         <subviews>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Amount:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IJW-uK-4cU">\n-                                <rect key="frame" x="16" y="84" width="65" height="21"/>\n+                                <rect key="frame" x="16" y="64" width="65" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="65" id="MD7-dK-jUx"/>\n                                     <constraint firstAttribute="height" constant="21" id="TzX-JE-21m"/>\n@@ -102,7 +100,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Fee:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZpH-5u-myO">\n-                                <rect key="frame" x="16" y="135" width="33" height="21"/>\n+                                <rect key="frame" x="16" y="115" width="33" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="33" id="IHo-Au-Vzg"/>\n                                     <constraint firstAttribute="height" constant="21" id="rGH-br-s9Y"/>\n@@ -112,7 +110,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Date:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YK6-7o-4uj">\n-                                <rect key="frame" x="16" y="186" width="41" height="21"/>\n+                                <rect key="frame" x="16" y="166" width="41" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="21" id="24z-gS-lPw"/>\n                                     <constraint firstAttribute="height" constant="21" id="EnQ-s2-uGi"/>\n@@ -124,7 +122,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sender:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vu4-vH-2qi">\n-                                <rect key="frame" x="16" y="237" width="60" height="21"/>\n+                                <rect key="frame" x="16" y="217" width="60" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="60" id="3EY-gN-DZ3"/>\n                                     <constraint firstAttribute="height" constant="21" id="MX3-6h-IrC"/>\n@@ -134,7 +132,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Receiver:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GNr-nd-ZLG">\n-                                <rect key="frame" x="16" y="288" width="71" height="21"/>\n+                                <rect key="frame" x="16" y="268" width="71" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="grr-0C-hLo"/>\n                                     <constraint firstAttribute="width" constant="71" id="nOS-2Q-fhg"/>\n@@ -144,7 +142,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Identifier:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1aL-0l-OyD">\n-                                <rect key="frame" x="16" y="339" width="73" height="21"/>\n+                                <rect key="frame" x="16" y="319" width="73" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="SQx-kY-zcL"/>\n                                     <constraint firstAttribute="width" constant="73" id="Zh7-Sw-dx6"/>\n@@ -154,7 +152,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Confirmation:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="paz-yI-Rkj">\n-                                <rect key="frame" x="16" y="390" width="104" height="21"/>\n+                                <rect key="frame" x="16" y="370" width="104" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="8Pg-AX-I75"/>\n                                     <constraint firstAttribute="width" constant="104" id="LBl-oP-Oym"/>\n@@ -164,7 +162,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="State:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Msp-ZU-V54">\n-                                <rect key="frame" x="16" y="441" width="54" height="21"/>\n+                                <rect key="frame" x="16" y="421" width="54" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="54" id="9tk-ew-zO5"/>\n                                     <constraint firstAttribute="height" constant="21" id="GNQ-aS-PmA"/>\n@@ -174,7 +172,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Actions:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="i29-Bu-x5Y">\n-                                <rect key="frame" x="16" y="492" width="76" height="21"/>\n+                                <rect key="frame" x="16" y="472" width="76" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="76" id="W4E-sP-Nfh"/>\n                                     <constraint firstAttribute="height" constant="21" id="dWg-mI-1mX"/>\n@@ -184,7 +182,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Nonce:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="F0N-Rw-q7C">\n-                                <rect key="frame" x="16" y="553" width="55" height="21"/>\n+                                <rect key="frame" x="16" y="533" width="55" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="meh-QZ-Dgs"/>\n                                     <constraint firstAttribute="width" constant="55" id="spc-k2-x5G"/>\n@@ -194,37 +192,37 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zpy-jG-U3g">\n-                                <rect key="frame" x="16" y="107" width="343" height="17"/>\n+                                <rect key="frame" x="16" y="87" width="343" height="17"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mx1-cF-5NT">\n-                                <rect key="frame" x="16" y="158" width="343" height="17"/>\n+                                <rect key="frame" x="16" y="138" width="343" height="17"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pSY-xF-fdu">\n-                                <rect key="frame" x="16" y="209" width="343" height="17"/>\n+                                <rect key="frame" x="16" y="189" width="343" height="17"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MLN-bC-1fP">\n-                                <rect key="frame" x="16" y="362" width="343" height="17"/>\n+                                <rect key="frame" x="16" y="342" width="343" height="17"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XO7-1V-dpo">\n-                                <rect key="frame" x="16" y="413" width="173" height="17"/>\n+                                <rect key="frame" x="16" y="393" width="173" height="17"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Yrm-2z-u7B">\n-                                <rect key="frame" x="16" y="464" width="343" height="17"/>\n+                                <rect key="frame" x="16" y="444" width="343" height="17"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="17" id="yc5-36-qlS"/>\n                                 </constraints>\n@@ -233,7 +231,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="W6B-wc-UDS">\n-                                <rect key="frame" x="16" y="576" width="343" height="17"/>\n+                                <rect key="frame" x="16" y="556" width="343" height="17"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="17" id="bXz-lE-ih8"/>\n                                 </constraints>\n@@ -242,7 +240,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="x5c-ne-Y7v">\n-                                <rect key="frame" x="16" y="515" width="156" height="30"/>\n+                                <rect key="frame" x="16" y="495" width="156" height="30"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="30" id="rpn-tz-UCH"/>\n                                     <constraint firstAttribute="width" constant="156" id="u8G-qD-4qj"/>\n@@ -253,7 +251,7 @@\n                                 </connections>\n                             </button>\n                             <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Fx0-Tm-aUD">\n-                                <rect key="frame" x="203" y="515" width="156" height="30"/>\n+                                <rect key="frame" x="203" y="495" width="156" height="30"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="156" id="Ayi-Gm-HbZ"/>\n                                     <constraint firstAttribute="height" constant="30" id="OtA-yS-yPL"/>\n@@ -264,7 +262,7 @@\n                                 </connections>\n                             </button>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Nna-wm-WQ9">\n-                                <rect key="frame" x="189" y="413" width="170" height="17"/>\n+                                <rect key="frame" x="189" y="393" width="170" height="17"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="170" id="Ky3-Hs-gQR"/>\n                                 </constraints>\n@@ -273,7 +271,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rRL-RR-uWP" customClass="Dot" customModule="BRCryptoDemo" customModuleProvider="target">\n-                                <rect key="frame" x="334" y="437" width="25" height="25"/>\n+                                <rect key="frame" x="334" y="417" width="25" height="25"/>\n                                 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="25" id="9D3-Ua-11c"/>\n@@ -281,14 +279,14 @@\n                                 </constraints>\n                             </view>\n                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hgu-ad-dm8">\n-                                <rect key="frame" x="16" y="257" width="343" height="30"/>\n+                                <rect key="frame" x="16" y="237" width="343" height="30"/>\n                                 <state key="normal" title="Button"/>\n                                 <connections>\n                                     <action selector="toPasteBoard:" destination="hSS-q9-i3e" eventType="touchUpInside" id="y5L-BX-RAZ"/>\n                                 </connections>\n                             </button>\n                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="H0c-iL-i9U">\n-                                <rect key="frame" x="16" y="308" width="343" height="30"/>\n+                                <rect key="frame" x="16" y="288" width="343" height="30"/>\n                                 <state key="normal" title="Button"/>\n                                 <connections>\n                                     <action selector="toPasteBoard:" destination="hSS-q9-i3e" eventType="touchUpInside" id="vl3-tc-3Fp"/>\n@@ -401,10 +399,10 @@\n                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>\n                         <prototypes>\n                             <tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="WalletCell" textLabel="gH9-Vu-gaM" detailTextLabel="uWa-Qg-EAH" style="IBUITableViewCellStyleValue1" id="WCw-Qf-5nD" customClass="WalletTableViewCell" customModule="BRCryptoDemo" customModuleProvider="target">\n-                                <rect key="frame" x="0.0" y="28" width="375" height="44"/>\n+                                <rect key="frame" x="0.0" y="28" width="375" height="43.5"/>\n                                 <autoresizingMask key="autoresizingMask"/>\n                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WCw-Qf-5nD" id="37f-cq-3Eg">\n-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>\n+                                    <rect key="frame" x="0.0" y="0.0" width="347.5" height="43.5"/>\n                                     <autoresizingMask key="autoresizingMask"/>\n                                     <subviews>\n                                         <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="gH9-Vu-gaM">\n@@ -415,7 +413,7 @@\n                                             <nil key="highlightedColor"/>\n                                         </label>\n                                         <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Amount" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="uWa-Qg-EAH">\n-                                            <rect key="frame" x="280" y="12" width="60" height="20.5"/>\n+                                            <rect key="frame" x="279.5" y="12" width="60" height="20.5"/>\n                                             <autoresizingMask key="autoresizingMask"/>\n                                             <fontDescription key="fontDescription" type="system" pointSize="17"/>\n                                             <nil key="textColor"/>\n@@ -436,11 +434,16 @@\n                         </connections>\n                     </tableView>\n                     <navigationItem key="navigationItem" title="Wallets" id="Zdf-7t-Un8">\n-                        <barButtonItem key="rightBarButtonItem" title="Act" style="plain" id="bJo-Sm-PQC">\n+                        <barButtonItem key="leftBarButtonItem" title="Act" style="plain" id="bJo-Sm-PQC">\n                             <connections>\n                                 <action selector="doAct:" destination="7bK-jq-Zjz" id="lqs-Nu-ACB"/>\n                             </connections>\n                         </barButtonItem>\n+                        <barButtonItem key="rightBarButtonItem" style="plain" systemItem="add" id="q3V-z1-OOb">\n+                            <connections>\n+                                <action selector="doAddWallet:" destination="7bK-jq-Zjz" id="mi9-g4-ZLa"/>\n+                            </connections>\n+                        </barButtonItem>\n                     </navigationItem>\n                     <connections>\n                         <outlet property="showManagersButton" destination="LPB-SX-Hlg" id="599-nL-sgh"/>\n@@ -463,7 +466,7 @@\n             <objects>\n                 <navigationController title="Transfer Navigator (Details)" id="vC3-pB-5Vb" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="DjV-YW-jjY">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\n@@ -487,7 +490,7 @@\n                                 <rect key="frame" x="0.0" y="28" width="375" height="100"/>\n                                 <autoresizingMask key="autoresizingMask"/>\n                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="rTY-o4-StW" id="HLA-Me-cLB" customClass="WalletTableViewCell" customModule="BRCryptoDemo" customModuleProvider="target">\n-                                    <rect key="frame" x="0.0" y="0.0" width="375" height="99.5"/>\n+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="100"/>\n                                     <autoresizingMask key="autoresizingMask"/>\n                                     <subviews>\n                                         <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bHi-Q7-GbW">\n@@ -557,7 +560,7 @@\n             <objects>\n                 <navigationController id="Gqh-9O-vAN" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="NpG-UA-hIX">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\n@@ -577,7 +580,7 @@\n                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>\n                         <subviews>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sync Mode" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fwc-Gi-dKy">\n-                                <rect key="frame" x="16" y="76" width="343" height="21"/>\n+                                <rect key="frame" x="16" y="56" width="343" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="P7f-wB-oGh"/>\n                                 </constraints>\n@@ -586,7 +589,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="BJL-OS-MjS">\n-                                <rect key="frame" x="16" y="105" width="343" height="31"/>\n+                                <rect key="frame" x="16" y="85" width="343" height="31"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="30" id="TXa-c2-Mab"/>\n                                 </constraints>\n@@ -601,7 +604,7 @@\n                                 </connections>\n                             </segmentedControl>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Address Scheme" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="m8f-sp-u5j">\n-                                <rect key="frame" x="16" y="168" width="343" height="21"/>\n+                                <rect key="frame" x="16" y="148" width="343" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="9Ie-D1-vMI"/>\n                                 </constraints>\n@@ -610,7 +613,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="QUW-82-OMc">\n-                                <rect key="frame" x="16" y="197" width="343" height="29"/>\n+                                <rect key="frame" x="16" y="177" width="343" height="32"/>\n                                 <segments>\n                                     <segment title="BTC_LEGACY"/>\n                                     <segment title="BTC_SEGWIT"/>\n@@ -622,7 +625,7 @@\n                                 </connections>\n                             </segmentedControl>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Connect State:" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qu7-co-zDc">\n-                                <rect key="frame" x="16" y="258" width="167.5" height="21"/>\n+                                <rect key="frame" x="16" y="241" width="167.5" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="K4m-If-2xg"/>\n                                 </constraints>\n@@ -631,7 +634,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Block Height:" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bQH-Yn-otr">\n-                                <rect key="frame" x="16" y="348" width="167.5" height="21"/>\n+                                <rect key="frame" x="16" y="331" width="167.5" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="FHj-lr-qYH"/>\n                                 </constraints>\n@@ -640,7 +643,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sync Progress:" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QjG-mV-nmB">\n-                                <rect key="frame" x="16" y="377" width="167.5" height="21"/>\n+                                <rect key="frame" x="16" y="360" width="167.5" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="9H4-cn-Mhj"/>\n                                 </constraints>\n@@ -649,7 +652,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="i03-zC-8Xu">\n-                                <rect key="frame" x="16" y="287" width="343" height="29"/>\n+                                <rect key="frame" x="16" y="270" width="343" height="29"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="28" id="Pba-qq-GkK"/>\n                                 </constraints>\n@@ -663,19 +666,19 @@\n                                 </connections>\n                             </segmentedControl>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rHY-dK-3fI">\n-                                <rect key="frame" x="191.5" y="258" width="167.5" height="21"/>\n+                                <rect key="frame" x="191.5" y="241" width="167.5" height="21"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="17"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Height" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ki6-b3-OPr">\n-                                <rect key="frame" x="191.5" y="348" width="168" height="21"/>\n+                                <rect key="frame" x="191.5" y="331" width="168" height="21"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="17"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="83c-4o-rSE">\n-                                <rect key="frame" x="183.5" y="279" width="8" height="0.0"/>\n+                                <rect key="frame" x="183.5" y="262" width="8" height="0.0"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="8" id="khI-cF-fAf"/>\n                                 </constraints>\n@@ -684,7 +687,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Progress" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2Kh-N7-iQd">\n-                                <rect key="frame" x="191.5" y="376" width="167.5" height="21"/>\n+                                <rect key="frame" x="191.5" y="359" width="167.5" height="21"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="17"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n@@ -832,7 +835,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="bXx-vx-ztK">\n-                                <rect key="frame" x="16" y="344" width="343" height="29"/>\n+                                <rect key="frame" x="16" y="344" width="343" height="32"/>\n                                 <segments>\n                                     <segment title="15"/>\n                                     <segment title="5"/>\n@@ -843,7 +846,7 @@\n                                 </connections>\n                             </segmentedControl>\n                             <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="Ei5-pP-B3a">\n-                                <rect key="frame" x="16" y="410" width="343" height="29"/>\n+                                <rect key="frame" x="16" y="410" width="343" height="32"/>\n                                 <segments>\n                                     <segment title="92000"/>\n                                     <segment title="21000"/>\n@@ -879,7 +882,7 @@\n                                 </connections>\n                             </segmentedControl>\n                             <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="AAl-Cv-lR0">\n-                                <rect key="frame" x="16" y="476" width="343" height="29"/>\n+                                <rect key="frame" x="16" y="476" width="343" height="32"/>\n                                 <segments>\n                                     <segment title="5000"/>\n                                     <segment title="2000"/>\n@@ -901,8 +904,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <textField opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="hkW-xm-lDZ">\n-                                <rect key="frame" x="16" y="121" width="343" height="30"/>\n-                                <nil key="textColor"/>\n+                                <rect key="frame" x="16" y="121" width="343" height="34"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>\n                                 <textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" keyboardAppearance="alert"/>\n                                 <connections>\n@@ -1013,7 +1015,7 @@\n             <objects>\n                 <navigationController storyboardIdentifier="createTransferRecvNC" id="FRX-cg-Und" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="su9-Mt-Ynn">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\n@@ -1033,14 +1035,14 @@\n                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>\n                         <subviews>\n                             <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="KJb-LE-FfD">\n-                                <rect key="frame" x="94.5" y="272.5" width="186" height="186"/>\n+                                <rect key="frame" x="94.5" y="262.5" width="186" height="186"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="186" id="nqz-vX-EfI"/>\n                                     <constraint firstAttribute="width" constant="186" id="th2-1a-3ZM"/>\n                                 </constraints>\n                             </imageView>\n                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="s3a-cM-jgZ">\n-                                <rect key="frame" x="16" y="482" width="343" height="30"/>\n+                                <rect key="frame" x="16" y="472" width="343" height="30"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="30" id="ch4-4z-E4f"/>\n                                 </constraints>\n@@ -1082,7 +1084,7 @@\n             <objects>\n                 <navigationController storyboardIdentifier="createTransferPayNC" id="TpV-6z-ayL" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Bg7-Mz-htM">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\n@@ -1102,7 +1104,7 @@\n                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>\n                         <subviews>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="BitPay URL" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hch-vX-y1U">\n-                                <rect key="frame" x="16" y="74" width="343" height="21"/>\n+                                <rect key="frame" x="16" y="54" width="343" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="a8H-SG-Ob9"/>\n                                 </constraints>\n@@ -1111,16 +1113,15 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="7r3-zD-QhE">\n-                                <rect key="frame" x="16" y="103" width="343" height="30"/>\n+                                <rect key="frame" x="16" y="83" width="343" height="30"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="30" id="hGa-Bg-QUP"/>\n                                 </constraints>\n-                                <nil key="textColor"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>\n                                 <textInputTraits key="textInputTraits"/>\n                             </textField>\n                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CBj-xv-0Zc">\n-                                <rect key="frame" x="16" y="141" width="115" height="30"/>\n+                                <rect key="frame" x="16" y="121" width="115" height="30"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="115" id="Kvk-qG-130"/>\n                                     <constraint firstAttribute="height" constant="30" id="Zw0-sJ-9al"/>\n@@ -1131,7 +1132,7 @@\n                                 </connections>\n                             </button>\n                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5kT-AQ-PPn">\n-                                <rect key="frame" x="182.5" y="141" width="10" height="30"/>\n+                                <rect key="frame" x="182.5" y="121" width="10" height="30"/>\n                                 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="30" id="WBy-2L-CZI"/>\n@@ -1139,7 +1140,7 @@\n                                 </constraints>\n                             </view>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Parsed:" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="x7C-gc-n26">\n-                                <rect key="frame" x="16" y="186" width="167" height="21"/>\n+                                <rect key="frame" x="16" y="166" width="167" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="5cH-FB-Fux"/>\n                                 </constraints>\n@@ -1148,7 +1149,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Obt-BF-VXy">\n-                                <rect key="frame" x="191" y="186" width="168" height="21"/>\n+                                <rect key="frame" x="191" y="166" width="168" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="RF5-HP-TJx"/>\n                                 </constraints>\n@@ -1206,7 +1207,7 @@\n             <objects>\n                 <navigationController storyboardIdentifier="createTransferSweepNC" id="lhx-Xf-cdI" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Jbo-ca-5x4">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\n@@ -1226,13 +1227,13 @@\n                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>\n                         <subviews>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Private Key" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xxf-lt-2PC">\n-                                <rect key="frame" x="16" y="70" width="343" height="21"/>\n+                                <rect key="frame" x="16" y="50" width="343" height="21"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="17"/>\n                                 <nil key="textColor"/>\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q8z-uy-2bY">\n-                                <rect key="frame" x="160.5" y="137" width="54" height="30"/>\n+                                <rect key="frame" x="160.5" y="117" width="54" height="30"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="30" id="6Gf-Uc-wvw"/>\n                                 </constraints>\n@@ -1242,16 +1243,15 @@\n                                 </connections>\n                             </button>\n                             <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="mmv-CK-Eb8">\n-                                <rect key="frame" x="16" y="99" width="343" height="30"/>\n+                                <rect key="frame" x="16" y="79" width="343" height="30"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="30" id="KkG-HI-ySD"/>\n                                 </constraints>\n-                                <nil key="textColor"/>\n                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>\n                                 <textInputTraits key="textInputTraits"/>\n                             </textField>\n                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IVu-4d-J4f">\n-                                <rect key="frame" x="16" y="137" width="96" height="30"/>\n+                                <rect key="frame" x="16" y="117" width="96" height="30"/>\n                                 <constraints>\n                                     <constraint firstAttribute="width" constant="96" id="1h9-Xh-rbp"/>\n                                     <constraint firstAttribute="height" constant="30" id="4ai-Rh-PeD"/>\n@@ -1262,10 +1262,10 @@\n                                 </connections>\n                             </button>\n                             <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="HWC-BL-Ug9">\n-                                <rect key="frame" x="339" y="142" width="20" height="20"/>\n+                                <rect key="frame" x="339" y="122" width="20" height="20"/>\n                             </activityIndicatorView>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Balance:" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Mn5-Nf-xU2">\n-                                <rect key="frame" x="16" y="220" width="162" height="21"/>\n+                                <rect key="frame" x="16" y="200" width="162" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="ZXv-JO-QKd"/>\n                                 </constraints>\n@@ -1274,7 +1274,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Fee:" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DFT-Tc-n59">\n-                                <rect key="frame" x="16" y="249" width="162" height="21"/>\n+                                <rect key="frame" x="16" y="229" width="162" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="hCM-T2-zCA"/>\n                                     <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="21" id="rBr-l0-os3"/>\n@@ -1284,7 +1284,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2Zq-Tj-BCK">\n-                                <rect key="frame" x="177.5" y="221" width="20" height="20"/>\n+                                <rect key="frame" x="177.5" y="201" width="20" height="20"/>\n                                 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="20" id="1Gr-h0-5Zs"/>\n@@ -1292,7 +1292,7 @@\n                                 </constraints>\n                             </view>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9y9-hP-V0Q">\n-                                <rect key="frame" x="199" y="221" width="160" height="21"/>\n+                                <rect key="frame" x="199" y="201" width="160" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" constant="21" id="65i-yb-RCC"/>\n                                 </constraints>\n@@ -1301,7 +1301,7 @@\n                                 <nil key="highlightedColor"/>\n                             </label>\n                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AiH-LC-Nwd">\n-                                <rect key="frame" x="199" y="250" width="160" height="21"/>\n+                                <rect key="frame" x="199" y="230" width="160" height="21"/>\n                                 <constraints>\n                                     <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="21" id="CLZ-iC-udV"/>\n                                     <constraint firstAttribute="height" constant="21" id="Rax-Vd-UfG"/>\n@@ -1372,7 +1372,7 @@\n             <objects>\n                 <navigationController storyboardIdentifier="createTransferSendNC" id="66z-us-aMt" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="fsm-4k-O4Y">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\n@@ -1388,7 +1388,7 @@\n             <objects>\n                 <navigationController title="Wallet Navigator (Transfers)" id="8T0-5N-1IS" sceneMemberID="viewController">\n                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="oG1-7X-XC4">\n-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>\n+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>\n                         <autoresizingMask key="autoresizingMask"/>\n                     </navigationBar>\n                     <connections>\ndiff --git a/Swift/BRCryptoDemo/CoreDemoAppDelegate.swift b/Swift/BRCryptoDemo/CoreDemoAppDelegate.swift\nindex d1fd1e6c..2634f0a7 100644\n--- a/Swift/BRCryptoDemo/CoreDemoAppDelegate.swift\n+++ b/Swift/BRCryptoDemo/CoreDemoAppDelegate.swift\n@@ -38,6 +38,9 @@ class CoreDemoAppDelegate: UIResponder, UIApplicationDelegate, UISplitViewContro\n \n     var query: BlockChainDB!\n \n+    var currencyCodesToMode: [String:WalletManagerMode]!\n+    var registerCurrencyCodes: [String]!\n+\n     var btcPeerSpec = (address: "103.99.168.100", port: UInt16(8333))\n     var btcPeer: NetworkPeer? = nil\n     var btcPeerUse = false\n@@ -90,26 +93,30 @@ class CoreDemoAppDelegate: UIResponder, UIApplicationDelegate, UISplitViewContro\n             .urls(for: .documentDirectory, in: .userDomainMask)[0]\n             .appendingPathComponent("Core").path\n \n-        if clearPersistentData {\n-            do {\n+        do {\n+            // If data shouild be cleared, then remove `storagePath`\n+            if clearPersistentData {\n                 if FileManager.default.fileExists(atPath: storagePath) {\n                     try FileManager.default.removeItem(atPath: storagePath)\n                 }\n-\n-                try FileManager.default.createDirectory (atPath: storagePath,\n-                                                         withIntermediateDirectories: true,\n-                                                         attributes: nil)\n-            }\n-            catch let error as NSError {\n-                print("Error: \\(error.localizedDescription)")\n             }\n+\n+            // Ensure that `storagePath` exists\n+            try FileManager.default.createDirectory (atPath: storagePath,\n+                                                     withIntermediateDirectories: true,\n+                                                     attributes: nil)\n+        }\n+        catch let error as NSError {\n+            print("Error: \\(error.localizedDescription)")\n         }\n+\n         \n         print ("APP: Account PaperKey  : \\(accountSpecification.paperKey.components(separatedBy: CharacterSet.whitespaces).first ?? "<missed>") ...")\n         print ("APP: Account Timestamp : \\(account.timestamp)")\n         print ("APP: StoragePath       : \\(storagePath?.description ?? "<none>")");\n         print ("APP: Mainnet           : \\(mainnet)")\n-        let currencyCodesToMode: [String:WalletManagerMode] = [\n+\n+        currencyCodesToMode = [\n             "btc" : .api_only,\n             "eth" : .api_only,\n //            "bch" : .p2p_only,\n@@ -122,7 +129,7 @@ class CoreDemoAppDelegate: UIResponder, UIApplicationDelegate, UISplitViewContro\n \n         }\n \n-        let registerCurrencyCodes = [\n+        registerCurrencyCodes = [\n             "ZLA",\n             "ADT"]\n \n@@ -130,18 +137,20 @@ class CoreDemoAppDelegate: UIResponder, UIApplicationDelegate, UISplitViewContro\n \n         // Create the listener\n         listener = CoreDemoListener (networkCurrencyCodesToMode: currencyCodesToMode,\n-                                         registerCurrencyCodes: registerCurrencyCodes,\n-                                         isMainnet: mainnet)\n+                                     registerCurrencyCodes: registerCurrencyCodes,\n+                                     isMainnet: mainnet)\n \n         // Create the BlockChainDB\n         query = BlockChainDB.createForTest ()\n \n         // Create the system\n-        self.system = System (listener: listener,\n-                              account: account,\n-                              onMainnet: mainnet,\n-                              path: storagePath,\n-                              query: query)\n+        self.system = System.create (listener: listener,\n+                                     account: account,\n+                                     onMainnet: mainnet,\n+                                     path: storagePath,\n+                                     query: query)\n+\n+        System.wipeAll (atPath: storagePath, except: [self.system])\n         \n         // Subscribe to notificiations or not (Provide an endpoint if notifications are enabled).\n         let subscriptionId = UIDevice.current.identifierForVendor!.uuidString\n@@ -161,13 +170,13 @@ class CoreDemoAppDelegate: UIResponder, UIApplicationDelegate, UISplitViewContro\n     func applicationDidEnterBackground(_ application: UIApplication) {\n         // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n         // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n-        system.stop()\n+        system.disconnectAll()\n     }\n \n     func applicationWillEnterForeground(_ application: UIApplication) {\n         // Called as part of the transition from the background to the active state; here you can\n         // undo many of the changes made on entering the background.\n-        system.managers.forEach { $0.connect() }\n+        system.connectAll()\n     }\n \n     func applicationDidBecomeActive(_ application: UIApplication) {\n@@ -200,13 +209,13 @@ extension UIApplication {\n         return (UIApplication.shared.delegate as! CoreDemoAppDelegate).accountSpecification.paperKey\n     }\n \n-    static func sync () {\n+    static func doSync () {\n         guard let app = UIApplication.shared.delegate as? CoreDemoAppDelegate else { return }\n         print ("APP: Syncing")\n         app.system.managers.forEach { $0.sync() }\n     }\n \n-    static func sleep() {\n+    static func doSleep () {\n         guard let app = UIApplication.shared.delegate as? CoreDemoAppDelegate else { return }\n         print ("APP: Disconnecting")\n         app.system.managers.forEach { $0.disconnect() }\n@@ -216,29 +225,87 @@ extension UIApplication {\n         }\n     }\n \n-    static func reset () {\n+    static func doReset () {\n         guard let app = UIApplication.shared.delegate as? CoreDemoAppDelegate else { return }\n         print ("APP: Resetting")\n \n-        // Create a new system\n-        let system = System (listener: app.listener!,\n-                             account: app.account,\n-                             onMainnet: app.mainnet,\n-                             path: app.storagePath,\n-                             query: app.query)\n-\n-        // Stop the existing system\n-        app.system.stop()\n+        // Clear out the UI.  Note: there is a race here on callbacks to the listener.  Something\n+        // in the UI *might* be created if an event occurs before the subsequent wipe completes.\n         app.summaryController.reset()\n \n-        \n-        // Assign and then configure the new system\n-        app.system = system\n+        // Destroy the current system.\n+        System.wipe(system: app.system);\n+\n+        // Again\n+        app.summaryController.reset()\n+\n+        // Remove the reference to the old system\n+        app.system = nil;\n+\n+        // Create a new system\n+        app.system = System.create (listener: app.listener!,\n+                                    account: app.account,\n+                                    onMainnet: app.listener.isMainnet,  // Wipe might change.\n+                                    path: app.storagePath,\n+                                    query: app.query)\n \n         // Passing `[]`... it is a demo app...\n         app.system.configure(withCurrencyModels: [])\n+\n+        // Too soon...\n+        app.summaryController.update()\n     }\n \n+    static func doWipe () {\n+        guard let app = UIApplication.shared.delegate as? CoreDemoAppDelegate else { return }\n+        print ("APP: Wiping")\n+\n+        let accountSpecificationsPath = Bundle(for: CoreDemoAppDelegate.self).path(forResource: "CoreTestsConfig", ofType: "json")!\n+        let accountSpecifications     = AccountSpecification.loadFrom(configPath: accountSpecificationsPath)\n+\n+        let alert = UIAlertController (title: "Select Paper Key",\n+                                       message: nil,\n+                                       preferredStyle: UIAlertController.Style.actionSheet)\n+\n+        accountSpecifications\n+            .forEach { (accountSpecification) in\n+                let action = UIAlertAction (title: accountSpecification.identifier, style: .default) { (action) in\n+                    app.summaryController.reset()\n+\n+                    System.wipe (system: app.system)\n+\n+                    let mainnet = (accountSpecification.network == "mainnet")\n+\n+                    app.account = Account.createFrom (phrase: accountSpecification.paperKey,\n+                                                      timestamp: accountSpecification.timestamp,\n+                                                      uids: "WalletID: \\(accountSpecification.identifier)")\n+\n+                    print ("APP: Account PaperKey  : \\(accountSpecification.paperKey.components(separatedBy: CharacterSet.whitespaces).first ?? "<missed>") ...")\n+                    print ("APP: Account Timestamp : \\(app.account.timestamp)")\n+                    print ("APP: Mainnet           : \\(mainnet)")\n+\n+                    // Create the listener\n+                    app.listener.isMainnet = mainnet\n+\n+                    app.system = System.create (listener: app.listener!,\n+                                                account: app.account,\n+                                                onMainnet: mainnet,\n+                                                path: app.storagePath,\n+                                                query: app.query)\n+\n+                    app.system.configure(withCurrencyModels: [])\n+                    alert.dismiss (animated: true) {\n+                        app.summaryController.reset()\n+                        app.summaryController.update()\n+                    }\n+                }\n+                alert.addAction (action)\n+        }\n+        alert.addAction (UIAlertAction (title: "Cancel", style: UIAlertAction.Style.cancel))\n+\n+        app.summaryController.present (alert, animated: true) {}\n+    }\n+    \n     static func peer (network: Network) -> NetworkPeer? {\n         guard let app = UIApplication.shared.delegate as? CoreDemoAppDelegate else { return nil }\n         guard Currency.codeAsBTC == network.currency.code else { return nil }\ndiff --git a/Swift/BRCryptoDemo/CoreDemoListener.swift b/Swift/BRCryptoDemo/CoreDemoListener.swift\nindex ff7b8060..6b3f04bf 100644\n--- a/Swift/BRCryptoDemo/CoreDemoListener.swift\n+++ b/Swift/BRCryptoDemo/CoreDemoListener.swift\n@@ -24,7 +24,7 @@ class CoreDemoListener: SystemListener {\n     private let networkCurrencyCodesToMode: [String:WalletManagerMode]\n     private let registerCurrencyCodes: [String]\n \n-    private let isMainnet: Bool\n+    internal var isMainnet: Bool\n \n     public init (networkCurrencyCodesToMode: [String:WalletManagerMode],\n                  registerCurrencyCodes: [String],\ndiff --git a/Swift/BRCryptoDemo/SummaryViewController.swift b/Swift/BRCryptoDemo/SummaryViewController.swift\nindex 8f8ca997..4f1d2eeb 100644\n--- a/Swift/BRCryptoDemo/SummaryViewController.swift\n+++ b/Swift/BRCryptoDemo/SummaryViewController.swift\n@@ -27,6 +27,13 @@ class SummaryViewController: UITableViewController, WalletListener {\n         }\n     }\n \n+    func update () {\n+        DispatchQueue.main.async {\n+            self.wallets = UIApplication.sharedSystem.wallets\n+            self.tableView.reloadData()\n+        }\n+    }\n+\n     override func viewDidLoad() {\n         super.viewDidLoad()\n \n@@ -37,14 +44,16 @@ class SummaryViewController: UITableViewController, WalletListener {\n     }\n \n     override func viewWillAppear (_ animated: Bool) {\n-        self.wallets = UIApplication.sharedSystem.wallets\n+        super.viewWillAppear(animated)\n \n         if let listener = UIApplication.sharedSystem.listener as? CoreDemoListener {\n             listener.add (walletListener: self)\n         }\n         \n         clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed\n-        super.viewWillAppear(animated)\n+\n+        // Reload the data - don\'t miss any wallets\n+        update ()\n     }\n \n     override func viewWillDisappear(_ animated: Bool) {\n@@ -89,12 +98,17 @@ class SummaryViewController: UITableViewController, WalletListener {\n                                        preferredStyle: UIAlertController.Style.alert)\n \n         alert.addAction (UIAlertAction (title: "Reset", style: UIAlertAction.Style.default) { (action) in\n-            UIApplication.reset()\n+            UIApplication.doReset()\n+            alert.dismiss(animated: true) {}\n+        })\n+\n+        alert.addAction (UIAlertAction (title: "Wipe", style: UIAlertAction.Style.default) { (action) in\n+            UIApplication.doWipe()\n             alert.dismiss(animated: true) {}\n         })\n \n         alert.addAction (UIAlertAction (title: "Sync", style: UIAlertAction.Style.default) { (action) in\n-            UIApplication.sync()\n+            UIApplication.doSync()\n             alert.dismiss(animated: true) {}\n         })\n \n@@ -104,7 +118,7 @@ class SummaryViewController: UITableViewController, WalletListener {\n         })\n \n         alert.addAction (UIAlertAction (title: "Sleep Eth", style: UIAlertAction.Style.default) { (action) in\n-            UIApplication.sleep()\n+            UIApplication.doSleep()\n             alert.dismiss(animated: true) {}\n         })\n \n@@ -113,6 +127,31 @@ class SummaryViewController: UITableViewController, WalletListener {\n         self.present (alert, animated: true) {}\n     }\n     \n+    @IBAction func doAddWallet(_ sender: Any) {\n+        let system = UIApplication.sharedSystem\n+        let currencies = system.managers\n+            .flatMap { $0.network.currencies }\n+            // Remove any currency already in `wallets`\n+            .filter { (c) in !wallets.contains { c == $0.currency } }\n+\n+        let alert = UIAlertController (title: "Add Wallet",\n+                                       message: nil,\n+                                       preferredStyle: UIAlertController.Style.actionSheet)\n+\n+        currencies.forEach { (c) in\n+            // The manager for (c)`\n+            if let manager = system.managers.first (where: { $0.network.hasCurrency(c) }) {\n+                alert.addAction (UIAlertAction (title: "\\(c.code) (\\(manager.name))", style: .default) { (action) in\n+                    let _ = manager.registerWalletFor(currency: c)\n+                    alert.dismiss(animated: true) {}\n+                })\n+            }\n+        }\n+\n+        alert.addAction (UIAlertAction (title: "Cancel", style: UIAlertAction.Style.cancel))\n+        self.present (alert, animated: true) {}\n+        \n+    }\n     // MARK: - Table View\n \n     override func numberOfSections(in tableView: UITableView) -> Int {\ndiff --git a/Swift/BRCryptoTests/BRCryptoAmountTests.swift b/Swift/BRCryptoTests/BRCryptoAmountTests.swift\nindex b2254754..ee8fa8fe 100644\n--- a/Swift/BRCryptoTests/BRCryptoAmountTests.swift\n+++ b/Swift/BRCryptoTests/BRCryptoAmountTests.swift\n@@ -116,8 +116,14 @@ class BRCryptoAmountTests: XCTestCase {\n         XCTAssert (-1.5       == btc4.double (as: BTC_BTC))\n         XCTAssert (-150000000 == btc4.double (as: BTC_SATOSHI))\n \n-        XCTAssert ("-B1.50"          == btc4.string (as: BTC_BTC))\n-        XCTAssert ("-SAT150,000,000" == btc4.string (as: BTC_SATOSHI))\n+        if #available(iOS 13, *) {\n+            XCTAssertEqual ("-B\xa01.50", btc4.string (as: BTC_BTC))\n+            XCTAssertEqual ("-SAT\xa0150,000,000", btc4.string (as: BTC_SATOSHI)!)\n+        }\n+        else {\n+            XCTAssertEqual ("-B1.50", btc4.string (as: BTC_BTC))\n+            XCTAssertEqual ("-SAT150,000,000", btc4.string (as: BTC_SATOSHI)!)\n+        }\n \n         XCTAssertEqual (btc1.double(as: BTC_BTC),     btc1.convert(to: BTC_SATOSHI)?.double(as: BTC_BTC))\n         XCTAssertEqual (btc1.double(as: BTC_SATOSHI), btc1.convert(to: BTC_BTC)?.double(as: BTC_SATOSHI))\n@@ -140,6 +146,18 @@ class BRCryptoAmountTests: XCTestCase {\n         XCTAssertTrue  ((eth2 - eth3)!.isNegative)\n         XCTAssertFalse ((eth2 - eth2)!.isNegative)\n \n+        let a1 = Amount.create (double: +1.0, unit: ETH_WEI)\n+        let a2 = Amount.create (double: -1.0, unit: ETH_WEI)\n+        XCTAssert (+0.0 == (a1 + a2)!.double (as: ETH_WEI));\n+        XCTAssert (+0.0 == (a2 + a1)!.double (as: ETH_WEI));\n+        XCTAssert (-2.0 == (a2 + a2)!.double (as: ETH_WEI));\n+        XCTAssert (+2.0 == (a1 + a1)!.double (as: ETH_WEI));\n+\n+        XCTAssert (+2.0 == (a1 - a2)!.double (as: ETH_WEI))\n+        XCTAssert (-2.0 == (a2 - a1)!.double (as: ETH_WEI))\n+        XCTAssert (+0.0 == (a1 - a1)!.double (as: ETH_WEI))\n+        XCTAssert (+0.0 == (a2 - a2)!.double (as: ETH_WEI))\n+\n         //\n         // String\n         //\n@@ -157,14 +175,27 @@ class BRCryptoAmountTests: XCTestCase {\n         XCTAssert (100000000 == btc3s.double (as: BTC_SATOSHI))\n         XCTAssert (1         == btc3s.double (as: BTC_BTC))\n \n-        XCTAssert ("SAT100,000,000" == btc3s.string (as: BTC_SATOSHI))\n-        XCTAssert ("B1.00"          == btc3s.string (as: BTC_BTC))\n+        if #available(iOS 13, *) {\n+            XCTAssertEqual ("SAT\xa0100,000,000", btc3s.string (as: BTC_SATOSHI)!)\n+            XCTAssertEqual ("B\xa01.00",          btc3s.string (as: BTC_BTC)!)\n+        }\n+        else {\n+            XCTAssertEqual ("SAT100,000,000", btc3s.string (as: BTC_SATOSHI)!)\n+            XCTAssertEqual ("B1.00",          btc3s.string (as: BTC_BTC)!)\n+        }\n \n         let btc4s = Amount.create (string: "0x5f5e100", negative: true, unit: BTC_SATOSHI)!\n         XCTAssert (-100000000 == btc4s.double (as: BTC_SATOSHI))\n         XCTAssert (-1         == btc4s.double (as: BTC_BTC))\n-        XCTAssert ("-SAT100,000,000" == btc4s.string (as: BTC_SATOSHI))\n-        XCTAssert ("-B1.00"          == btc4s.string (as: BTC_BTC))\n+\n+        if #available(iOS 13, *) {\n+            XCTAssertEqual ("-SAT\xa0100,000,000", btc4s.string (as: BTC_SATOSHI)!)\n+            XCTAssertEqual ("-B\xa01.00",          btc4s.string (as: BTC_BTC)!)\n+        }\n+        else {\n+            XCTAssertEqual ("-SAT100,000,000", btc4s.string (as: BTC_SATOSHI)!)\n+            XCTAssertEqual ("-B1.00",          btc4s.string (as: BTC_BTC)!)\n+        }\n \n         XCTAssertNil (Amount.create (string: "w0x5f5e100", unit: BTC_SATOSHI))\n         XCTAssertNil (Amount.create (string: "0x5f5e100w", unit: BTC_SATOSHI))\n@@ -226,7 +257,12 @@ class BRCryptoAmountTests: XCTestCase {\n         XCTAssertNotNil (a5?.double(as: ETH_WEI))\n         XCTAssertEqual(1234567891234567891, a5?.double(as: ETH_WEI)!)\n         // Lost precision - last 5 digits\n-        XCTAssertEqual("wei1,234,567,891,234,570,000", a5?.string(as: ETH_WEI)!)\n+        if #available(iOS 13, *) {\n+            XCTAssertEqual("wei\xa01,234,567,891,234,568,000", a5?.string(as: ETH_WEI)!)\n+        }\n+        else {\n+            XCTAssertEqual("wei1,234,567,891,234,570,000", a5?.string(as: ETH_WEI)!)\n+        }\n \n         XCTAssertEqual("1234567891234567891", a5?.string (base: 10, preface: ""))\n         XCTAssertEqual("1000000000000000000", Amount.create(string: "1", negative: false, unit: ETH_ETHER)!.string (base: 10, preface: ""))\ndiff --git a/Swift/BRCryptoTests/BRCryptoCommonTests.swift b/Swift/BRCryptoTests/BRCryptoCommonTests.swift\nindex df065c18..ebfeea2c 100644\n--- a/Swift/BRCryptoTests/BRCryptoCommonTests.swift\n+++ b/Swift/BRCryptoTests/BRCryptoCommonTests.swift\n@@ -92,7 +92,7 @@ class BRCryptoCommonTests: XCTestCase {\n         XCTAssertEqual (t, l.encodeAsPublic)\n \n         XCTAssertTrue (l.publicKeyMatch(k))\n-        \n+\n         //\n         // Bad Key\n         //\n@@ -226,9 +226,9 @@ class BRCryptoCommonTests: XCTestCase {\n \n     func testEncoder () {\n         var d: Data!\n-        var a: String\n-        var r: String\n-        var s: String\n+        var a: String!\n+        var r: String!\n+        var s: String!\n \n         // HEX\n         d = Data([0xde, 0xad, 0xbe, 0xef])\n@@ -237,6 +237,9 @@ class BRCryptoCommonTests: XCTestCase {\n         XCTAssertEqual (a, r)\n         XCTAssertEqual (d, CoreCoder.hex.decode(string: r))\n \n+        s = "#&$@*^(*#!^"\n+        XCTAssertNil (CoreCoder.hex.decode(string: s))\n+\n         // base58\n         s = "#&$@*^(*#!^"\n         XCTAssertNil (CoreCoder.base58.decode(string: s))\n@@ -258,6 +261,9 @@ class BRCryptoCommonTests: XCTestCase {\n         d = Data([0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])\n         s = CoreCoder.base58check.encode(data: d)\n         XCTAssertEqual (d, CoreCoder.base58check.decode(string: s))\n+\n+        s = "#&$@*^(*#!^"\n+        XCTAssertNil (CoreCoder.base58check.decode(string: s))\n     }\n \n     func testEncryptor () {\n@@ -269,8 +275,8 @@ class BRCryptoCommonTests: XCTestCase {\n         // aes-ecb\n         k = Data([0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c]) // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])\n         d = Data ([0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10])\n-        a = CoreEncrypter.aes_ecb(key: k).encrypt(data: d)\n-        XCTAssertEqual (d, CoreEncrypter.aes_ecb(key: k).decrypt(data: a))\n+        a = CoreCipher.aes_ecb(key: k).encrypt(data: d)\n+        XCTAssertEqual (d, CoreCipher.aes_ecb(key: k).decrypt(data: a))\n \n         // cha-cha\n         let secret = UInt256.init(u8: (0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f))\n@@ -279,7 +285,7 @@ class BRCryptoCommonTests: XCTestCase {\n         let ad: Data = Data([0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7])\n         let msg: Data! = "Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it."\n             .data (using: .utf8)\n-        let alg = CoreEncrypter.chacha20_poly1305 (key: key, nonce12: nonce12, ad: ad)\n+        let alg = CoreCipher.chacha20_poly1305 (key: key, nonce12: nonce12, ad: ad)\n \n         let cipher = Data([0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16, 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91])\n         XCTAssertEqual (cipher, alg.encrypt(data: msg))\n@@ -287,8 +293,8 @@ class BRCryptoCommonTests: XCTestCase {\n \n         // pigeon\n         let pubKey = Key.createFromString(asPublic: "02d404943960a71535a79679f1cf1df80e70597c05b05722839b38ebc8803af517")!\n-        let pigeon = CoreEncrypter.pigeon (privKey: key, pubKey: pubKey, nonce12: nonce12)\n-        let pigeonCipher = pigeon.encrypt(data: msg)\n+        let pigeon = CoreCipher.pigeon (privKey: key, pubKey: pubKey, nonce12: nonce12)\n+        let pigeonCipher = pigeon.encrypt(data: msg)!\n         XCTAssertEqual (pigeon.decrypt(data: pigeonCipher), msg)\n     }\n \n@@ -301,7 +307,7 @@ class BRCryptoCommonTests: XCTestCase {\n \n         let key = Key (\n             secret: UInt256.init(u8: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)))\n-        \n+\n         // Basic DER-encoded\n         msg = "How wonderful that we have met with a paradox. Now we have some hope of making progress."\n         digest = CoreHasher.sha256.hash(data: msg.data(using: .utf8)!)\n@@ -310,7 +316,7 @@ class BRCryptoCommonTests: XCTestCase {\n \n         answer = Data([0x30, 0x45, 0x02, 0x21, 0x00, 0xc0, 0xda, 0xfe, 0xc8, 0x25, 0x1f, 0x1d, 0x50, 0x10, 0x28, 0x9d, 0x21, 0x02, 0x32, 0x22, 0x0b, 0x03, 0x20, 0x2c, 0xba, 0x34, 0xec, 0x11, 0xfe, 0xc5, 0x8b, 0x3e, 0x93, 0xa8, 0x5b, 0x91, 0xd3, 0x02, 0x20, 0x75, 0xaf, 0xdc, 0x06, 0xb7, 0xd6, 0x32, 0x2a, 0x59, 0x09, 0x55, 0xbf, 0x26, 0x4e, 0x7a, 0xaa, 0x15, 0x58, 0x47, 0xf6, 0x14, 0xd8, 0x00, 0x78, 0xa9, 0x02, 0x92, 0xfe, 0x20, 0x50, 0x64, 0xd3])\n         XCTAssertEqual(answer, signature)\n-        \n+\n         // Basic raw\n         digest = CoreHasher.sha256.hash(data: msg.data(using: .utf8)!)\n         signer = CoreSigner.basicJOSE\n@@ -347,14 +353,13 @@ class BRCryptoCommonTests: XCTestCase {\n         ]\n \n         let message =  "Very deterministic message".data(using: .utf8)\n-            .map { CoreHasher.sha256_2.hash(data: $0 ) }\n-        XCTAssertNotNil(message)\n+        let digest = CoreHasher.sha256_2.hash(data: message!)\n \n         zip (secrets, signatures).forEach { secret, signature in\n             let key = Key.createFromString (asPrivate: secret)\n             XCTAssertNotNil(key)\n \n-            let outputSig = CoreSigner.compact.sign(data32: message!, using: key!)\n+            let outputSig = CoreSigner.compact.sign(data32: digest!, using: key!)!\n             //print (CoreCoder.hex.encode(data: outputSig))\n             XCTAssert(CoreCoder.hex.encode(data: outputSig) == signature)\n         }\ndiff --git a/Swift/Core/module.modulemap b/Swift/Core/module.modulemap\nindex d7426200..e35af6df 100644\n--- a/Swift/Core/module.modulemap\n+++ b/Swift/Core/module.modulemap\n@@ -6,7 +6,11 @@ module BRCryptoC [system] [extern_c] {\n     header "../../crypto/BRCryptoNetwork.h"\n     header "../../crypto/BRCryptoPeer.h"\n     header "../../crypto/BRCryptoAccount.h"\n+    header "../../crypto/BRCryptoCoder.h"\n+    header "../../crypto/BRCryptoCipher.h"\n     header "../../crypto/BRCryptoKey.h"\n+    header "../../crypto/BRCryptoHasher.h"\n+    header "../../crypto/BRCryptoSigner.h"\n     header "../../crypto/BRCryptoAddress.h"\n     header "../../crypto/BRCryptoFeeBasis.h"\n     header "../../crypto/BRCryptoTransfer.h"\ndiff --git a/bcash/BRBCashParams.c b/bcash/BRBCashParams.c\nindex c950a62d..ee6ff5ea 100644\n--- a/bcash/BRBCashParams.c\n+++ b/bcash/BRBCashParams.c\n@@ -29,13 +29,23 @@\n #include "BRBCashParams.h"\n \n static const char *BRBCashDNSSeeds[] = {\n-    "seed-abc.breadwallet.com.", "seed.bitcoinabc.org.", "seed-abc.bitcoinforks.org.", "seed.bitcoinunlimited.info.",\n-    "seed.bitprim.org.", "seed.deadalnix.me.", NULL\n+    "seed-abc.breadwallet.com.",\n+    "seed.bitcoinabc.org.",\n+    "seed-abc.bitcoinforks.org.",\n+    "seed.bitcoinunlimited.info.",\n+    "seed.bitprim.org.",\n+    "seed.deadalnix.me.",\n+    NULL\n };\n \n static const char *BRBCashTestNetDNSSeeds[] = {\n-    "testnet-seed.bitcoinabc.org", "testnet-seed-abc.bitcoinforks.org", "testnet-seed.bitprim.org",\n-    "testnet-seed.deadalnix.me", "testnet-seeder.criptolayer.net", NULL\n+    "testnet-seed-abc.breadwallet.com",\n+    "testnet-seed.bitcoinabc.org",\n+    "testnet-seed-abc.bitcoinforks.org",\n+    "testnet-seed.bitprim.org",\n+    "testnet-seed.deadalnix.me",\n+    "testnet-seeder.criptolayer.net",\n+    NULL\n };\n \n static const BRCheckPoint BRBCashTestNetCheckpoints[] = {\ndiff --git a/bitcoin/BRWalletManager.c b/bitcoin/BRWalletManager.c\nindex c3733ba9..f5cf1a29 100644\n--- a/bitcoin/BRWalletManager.c\n+++ b/bitcoin/BRWalletManager.c\n@@ -787,12 +787,14 @@ bwmFileServiceErrorHandler (BRFileServiceContext context,\n             break;\n         case FILE_SERVICE_ENTITY:\n             // This is likely a coding error too.\n-            _peer_log ("bread: FileService Error: ENTITY (%s); %s",\n+            _peer_log ("bread: FileService Error: ENTITY (%s): %s",\n                      error.u.entity.type,\n                      error.u.entity.reason);\n             break;\n         case FILE_SERVICE_SDB:\n-            _peer_log ("bread: FileService Error: SDB: %d", error.u.sdb.code);\n+            _peer_log ("bread: FileService Error: SDB: (%d): %s",\n+                       error.u.sdb.code,\n+                       error.u.sdb.reason);\n             break;\n     }\n     _peer_log ("bread: FileService Error: FORCED SYNC%s", "");\n@@ -1076,6 +1078,7 @@ extern void\n BRWalletManagerStop (BRWalletManager manager) {\n     BRWalletManagerDisconnect (manager);\n     eventHandlerStop (manager->handler);\n+    fileServiceClose (manager->fileService);\n }\n \n extern BRWallet *\ndiff --git a/crypto/BRCryptoAccount.c b/crypto/BRCryptoAccount.c\nindex 30c169d0..769fbc38 100644\n--- a/crypto/BRCryptoAccount.c\n+++ b/crypto/BRCryptoAccount.c\n@@ -363,6 +363,7 @@ cryptoAccountGetFileSystemIdentifier (BRCryptoAccount account) {\n     // Double SHA the serialization\n     UInt256 hash;\n     BRSHA256_2(&hash, mpkBytes, mpkSize);\n+    free (mpkBytes);\n \n     // Take the first 32 characters.\n     return strndup(u256hex(hash), 32);\ndiff --git a/crypto/BRCryptoAmount.c b/crypto/BRCryptoAmount.c\nindex 883474ba..480feca9 100644\n--- a/crypto/BRCryptoAmount.c\n+++ b/crypto/BRCryptoAmount.c\n@@ -175,14 +175,34 @@ cryptoAmountAdd (BRCryptoAmount a1,\n     assert (CRYPTO_TRUE == cryptoAmountIsCompatible (a1, a2));\n \n     int overflow = 0;\n-    UInt256 sum = addUInt256_Overflow(a1->value, a2->value, &overflow);\n+    int negative = 0;\n \n-    return overflow ? NULL : cryptoAmountCreate (a1->unit, CRYPTO_FALSE, sum);\n+    if (CRYPTO_TRUE == a1->isNegative && CRYPTO_TRUE != a2->isNegative) {\n+        // (-x) + y = (y - x)\n+        UInt256 value = subUInt256_Negative (a2->value, a1->value, &negative);\n+        return cryptoAmountCreate (a1->unit, AS_CRYPTO_BOOLEAN(negative), value);\n+    }\n+    else if (CRYPTO_TRUE != a1->isNegative && CRYPTO_TRUE == a2->isNegative) {\n+        // x + (-y) = x - y\n+        UInt256 value = subUInt256_Negative (a1->value, a2->value, &negative);\n+        return cryptoAmountCreate(a1->unit, AS_CRYPTO_BOOLEAN(negative), value);\n+    }\n+    else if (CRYPTO_TRUE == a1->isNegative && CRYPTO_TRUE == a2->isNegative) {\n+        // (-x) + (-y) = - (x + y)\n+        UInt256 value = addUInt256_Overflow (a2->value, a1->value, &overflow);\n+        return overflow ? NULL :  cryptoAmountCreate (a1->unit, CRYPTO_TRUE, value);\n+    }\n+    else {\n+        UInt256 value = addUInt256_Overflow (a1->value, a2->value, &overflow);\n+        return overflow ? NULL :  cryptoAmountCreate (a1->unit, CRYPTO_FALSE, value);\n+    }\n }\n \n extern BRCryptoAmount\n cryptoAmountSub (BRCryptoAmount a1,\n                  BRCryptoAmount a2) {\n+    assert (CRYPTO_TRUE == cryptoAmountIsCompatible (a1, a2));\n+\n     int overflow = 0;\n     int negative = 0;\n \ndiff --git a/crypto/BRCryptoCipher.c b/crypto/BRCryptoCipher.c\nnew file mode 100644\nindex 00000000..6b9885a7\n--- /dev/null\n+++ b/crypto/BRCryptoCipher.c\n@@ -0,0 +1,358 @@\n+//\n+//  BRCryptoCipher.c\n+//  BRCore\n+//\n+//  Created by Michael Carrara on 9/23/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#include "BRCryptoCipher.h"\n+#include "BRCryptoKey.h"\n+#include "support/BRBase.h"\n+#include "support/BRCrypto.h"\n+#include "support/BRKeyECIES.h"\n+\n+static void\n+cryptoCipherRelease (BRCryptoCipher cipher);\n+\n+struct BRCryptoCipherRecord {\n+    BRCryptoCipherType type;\n+\n+    union {\n+        struct {\n+            uint8_t key[32];\n+            size_t keyLen;\n+        } aesecb;\n+\n+        struct {\n+            BRCryptoKey key;\n+            uint8_t nonce[12];\n+            uint8_t *ad;\n+            size_t adLen;\n+        } chacha20;\n+\n+        struct {\n+            BRCryptoKey privKey;\n+            BRCryptoKey pubKey;\n+            uint8_t nonce[12];\n+        } pigeon;\n+    } u;\n+\n+    BRCryptoRef ref;\n+};\n+\n+static BRCryptoCipher\n+cryptoCipherCreateInternal(BRCryptoCipherType type) {\n+    BRCryptoCipher cipher = calloc (1, sizeof(struct BRCryptoCipherRecord));\n+    cipher->type = type;\n+    cipher->ref = CRYPTO_REF_ASSIGN(cryptoCipherRelease);\n+    return cipher;\n+}\n+\n+extern BRCryptoCipher\n+cryptoCipherCreateForAESECB(const uint8_t *key,\n+                            size_t keyLen) {\n+    // argument check; early exit\n+    if (NULL == key || (keyLen != 16 && keyLen != 24 && keyLen != 32)) {\n+        assert (0);\n+        return NULL;\n+    }\n+\n+    BRCryptoCipher cipher  = cryptoCipherCreateInternal (CRYPTO_CIPHER_AESECB);\n+    // aesecb.key initialized to zero via calloc\n+    memcpy (cipher->u.aesecb.key, key, keyLen);\n+    cipher->u.aesecb.keyLen = keyLen;\n+\n+    return cipher;\n+}\n+\n+extern BRCryptoCipher\n+cryptoCipherCreateForChacha20Poly1305(BRCryptoKey key,\n+                                      const uint8_t *nonce, size_t nonceLen,\n+                                      const uint8_t *ad, size_t adLen) {\n+    // argument check; early exit\n+    if (NULL == key || NULL == nonce || nonceLen != 12 ||\n+        (NULL == ad && 0 != adLen)) {\n+        assert (0);\n+        return NULL;\n+    }\n+\n+    BRCryptoCipher cipher  = cryptoCipherCreateInternal (CRYPTO_CIPHER_CHACHA20_POLY1305);\n+    cipher->u.chacha20.key = cryptoKeyTake (key);\n+    memcpy (cipher->u.chacha20.nonce, nonce, nonceLen);\n+    if (NULL != ad && 0 != adLen) {\n+        cipher->u.chacha20.ad = malloc (adLen);\n+        cipher->u.chacha20.adLen = adLen;\n+        memcpy (cipher->u.chacha20.ad, ad, adLen);\n+    }\n+\n+    return cipher;\n+}\n+\n+extern BRCryptoCipher\n+cryptoCipherCreateForPigeon(BRCryptoKey privKey,\n+                            BRCryptoKey pubKey,\n+                            const uint8_t *nonce, size_t nonceLen) {\n+    // argument check; early exit\n+    if (NULL == pubKey || NULL == privKey || NULL == nonce || nonceLen != 12) {\n+        assert (0);\n+        return NULL;\n+    }\n+\n+    BRCryptoCipher cipher  = cryptoCipherCreateInternal (CRYPTO_CIPHER_PIGEON);\n+    cipher->u.pigeon.privKey = cryptoKeyTake (privKey);\n+    cipher->u.pigeon.pubKey = cryptoKeyTake (pubKey);\n+    memcpy (cipher->u.pigeon.nonce, nonce, nonceLen);\n+\n+    return cipher;\n+}\n+\n+static void\n+cryptoCipherRelease (BRCryptoCipher cipher) {\n+    switch (cipher->type) {\n+        case CRYPTO_CIPHER_AESECB: {\n+            break;\n+        }\n+        case CRYPTO_CIPHER_CHACHA20_POLY1305: {\n+            cryptoKeyGive (cipher->u.chacha20.key);\n+            if (NULL != cipher->u.chacha20.ad) {\n+                free (cipher->u.chacha20.ad);\n+            }\n+            break;\n+        }\n+        case CRYPTO_CIPHER_PIGEON: {\n+            cryptoKeyGive (cipher->u.pigeon.privKey);\n+            cryptoKeyGive (cipher->u.pigeon.pubKey);\n+            break;\n+        }\n+        default: {\n+            break;\n+        }\n+    }\n+\n+    memset (cipher, 0, sizeof(*cipher));\n+    free (cipher);\n+}\n+\n+extern size_t\n+cryptoCipherEncryptLength (BRCryptoCipher cipher,\n+                           const uint8_t *src,\n+                           size_t srcLen) {\n+    // - src CAN be NULL, if srcLen is 0\n+    if (NULL == src && 0 != srcLen) {\n+        assert (0);\n+        return 0;\n+    }\n+\n+    size_t length = 0;\n+    switch (cipher->type) {\n+        case CRYPTO_CIPHER_AESECB: {\n+            length = (0 == srcLen % 16) ? srcLen : 0;\n+            break;\n+        }\n+        case CRYPTO_CIPHER_CHACHA20_POLY1305: {\n+            UInt256 secret = cryptoKeyGetSecret (cipher->u.chacha20.key);\n+            length = BRChacha20Poly1305AEADEncrypt (NULL,\n+                                                    0,\n+                                                    &secret,\n+                                                    cipher->u.chacha20.nonce,\n+                                                    src,\n+                                                    srcLen,\n+                                                    cipher->u.chacha20.ad,\n+                                                    cipher->u.chacha20.adLen);\n+            secret = UINT256_ZERO; (void) &secret;\n+            break;\n+        }\n+        case CRYPTO_CIPHER_PIGEON: {\n+            length = BRKeyPigeonEncrypt (NULL,\n+                                         NULL,\n+                                         0,\n+                                         NULL,\n+                                         cipher->u.pigeon.nonce,\n+                                         src,\n+                                         srcLen);\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return length;\n+}\n+\n+extern BRCryptoBoolean\n+cryptoCipherEncrypt (BRCryptoCipher cipher,\n+                     uint8_t *dst,\n+                     size_t dstLen,\n+                     const uint8_t *src,\n+                     size_t srcLen) {\n+    // - src CAN be NULL, if srcLen is 0\n+    // - dst MUST be non-NULL and sufficiently sized\n+    if ((NULL == src && 0 != srcLen) ||\n+        NULL == dst || dstLen < cryptoCipherEncryptLength (cipher, src, srcLen)) {\n+        assert (0);\n+        return CRYPTO_FALSE;\n+    }\n+\n+    BRCryptoBoolean result = CRYPTO_FALSE;\n+\n+    switch (cipher->type) {\n+        case CRYPTO_CIPHER_AESECB: {\n+            if (srcLen == dstLen && (0 == srcLen % 16)) {\n+                memcpy (dst, src, dstLen);\n+                for (size_t index = 0; index < dstLen; index += 16) {\n+                    BRAESECBEncrypt (&dst[index], cipher->u.aesecb.key, cipher->u.aesecb.keyLen);\n+                }\n+                result = CRYPTO_TRUE;\n+            }\n+            break;\n+        }\n+        case CRYPTO_CIPHER_CHACHA20_POLY1305: {\n+            UInt256 secret = cryptoKeyGetSecret (cipher->u.chacha20.key);\n+            result = AS_CRYPTO_BOOLEAN (BRChacha20Poly1305AEADEncrypt (dst,\n+                                                                       dstLen,\n+                                                                       &secret,\n+                                                                       cipher->u.chacha20.nonce,\n+                                                                       src,\n+                                                                       srcLen,\n+                                                                       cipher->u.chacha20.ad,\n+                                                                       cipher->u.chacha20.adLen));\n+            secret = UINT256_ZERO; (void) &secret;\n+            break;\n+        }\n+        case CRYPTO_CIPHER_PIGEON: {\n+            result = AS_CRYPTO_BOOLEAN (BRKeyPigeonEncrypt (cryptoKeyGetCore (cipher->u.pigeon.privKey),\n+                                                            dst,\n+                                                            dstLen,\n+                                                            cryptoKeyGetCore (cipher->u.pigeon.pubKey),\n+                                                            cipher->u.pigeon.nonce,\n+                                                            src,\n+                                                            srcLen));\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return result;\n+}\n+\n+extern size_t\n+cryptoCipherDecryptLength (BRCryptoCipher cipher,\n+                           const char *src,\n+                           size_t srcLen) {\n+    // - src CAN be NULL, if srcLen is 0\n+    if (NULL == src && 0 != srcLen) {\n+        assert (0);\n+        return 0;\n+    }\n+\n+    size_t length = 0;\n+\n+    switch (cipher->type) {\n+        case CRYPTO_CIPHER_AESECB: {\n+            length = (0 == srcLen % 16) ? srcLen : 0;\n+            break;\n+        }\n+        case CRYPTO_CIPHER_CHACHA20_POLY1305: {\n+            UInt256 secret = cryptoKeyGetSecret (cipher->u.chacha20.key);\n+            length = BRChacha20Poly1305AEADDecrypt (NULL,\n+                                                    0,\n+                                                    &secret,\n+                                                    cipher->u.chacha20.nonce,\n+                                                    src,\n+                                                    srcLen,\n+                                                    cipher->u.chacha20.ad,\n+                                                    cipher->u.chacha20.adLen);\n+            secret = UINT256_ZERO; (void) &secret;\n+            break;\n+        }\n+        case CRYPTO_CIPHER_PIGEON: {\n+            length = BRKeyPigeonDecrypt (NULL,\n+                                         NULL,\n+                                         0,\n+                                         NULL,\n+                                         cipher->u.pigeon.nonce,\n+                                         src,\n+                                         srcLen);\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return length;\n+}\n+\n+extern BRCryptoBoolean\n+cryptoCipherDecrypt (BRCryptoCipher cipher,\n+                     uint8_t *dst,\n+                     size_t dstLen,\n+                     const char *src,\n+                     size_t srcLen) {\n+    // - src CAN be NULL, if srcLen is 0\n+    // - dst MUST be non-NULL and sufficiently sized\n+    if ((NULL == src && 0 != srcLen) ||\n+        NULL == dst || dstLen < cryptoCipherDecryptLength (cipher, src, srcLen)) {\n+        assert (0);\n+        return CRYPTO_FALSE;\n+    }\n+\n+    BRCryptoBoolean result = CRYPTO_FALSE;\n+\n+    switch (cipher->type) {\n+        case CRYPTO_CIPHER_AESECB: {\n+            if (srcLen == dstLen && (0 == srcLen % 16)) {\n+                memcpy (dst, src, dstLen);\n+                for (size_t index = 0; index < dstLen; index += 16) {\n+                    BRAESECBDecrypt (&dst[index], cipher->u.aesecb.key, cipher->u.aesecb.keyLen);\n+                }\n+                result = CRYPTO_TRUE;\n+            }\n+            break;\n+        }\n+        case CRYPTO_CIPHER_CHACHA20_POLY1305: {\n+            UInt256 secret = cryptoKeyGetSecret (cipher->u.chacha20.key);\n+            result = AS_CRYPTO_BOOLEAN (BRChacha20Poly1305AEADDecrypt (dst,\n+                                                                       dstLen,\n+                                                                       &secret,\n+                                                                       cipher->u.chacha20.nonce,\n+                                                                       src,\n+                                                                       srcLen,\n+                                                                       cipher->u.chacha20.ad,\n+                                                                       cipher->u.chacha20.adLen));\n+            secret = UINT256_ZERO; (void) &secret;\n+            break;\n+        }\n+        case CRYPTO_CIPHER_PIGEON: {\n+            result = AS_CRYPTO_BOOLEAN (BRKeyPigeonDecrypt (cryptoKeyGetCore (cipher->u.pigeon.privKey),\n+                                                            dst,\n+                                                            dstLen,\n+                                                            cryptoKeyGetCore (cipher->u.pigeon.pubKey),\n+                                                            cipher->u.pigeon.nonce,\n+                                                            src,\n+                                                            srcLen));\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return result;\n+}\n+\n+IMPLEMENT_CRYPTO_GIVE_TAKE (BRCryptoCipher, cryptoCipher);\ndiff --git a/crypto/BRCryptoCipher.h b/crypto/BRCryptoCipher.h\nnew file mode 100644\nindex 00000000..54e62d58\n--- /dev/null\n+++ b/crypto/BRCryptoCipher.h\n@@ -0,0 +1,73 @@\n+//\n+//  BRCryptoCipher.h\n+//  BRCore\n+//\n+//  Created by Michael Carrara on 9/23/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#ifndef BRCryptoCipher_h\n+#define BRCryptoCipher_h\n+\n+#include "BRCryptoBase.h"\n+#include "BRCryptoKey.h"\n+\n+#ifdef __cplusplus\n+extern "C" {\n+#endif\n+\n+    typedef enum {\n+        CRYPTO_CIPHER_AESECB,\n+        CRYPTO_CIPHER_CHACHA20_POLY1305,\n+        CRYPTO_CIPHER_PIGEON\n+    } BRCryptoCipherType;\n+\n+    typedef struct BRCryptoCipherRecord *BRCryptoCipher;\n+\n+    extern BRCryptoCipher\n+    cryptoCipherCreateForAESECB(const uint8_t *key,\n+                                size_t keyLen);\n+\n+    extern BRCryptoCipher\n+    cryptoCipherCreateForChacha20Poly1305(BRCryptoKey key,\n+                                          const uint8_t *nonce, size_t nonceLen,\n+                                          const uint8_t *authenticatedData, size_t authenticatedDataLen);\n+\n+    extern BRCryptoCipher\n+    cryptoCipherCreateForPigeon(BRCryptoKey privKey,\n+                                BRCryptoKey pubKey,\n+                                const uint8_t *nonce, size_t nonceLen);\n+\n+    extern size_t\n+    cryptoCipherEncryptLength (BRCryptoCipher cipher,\n+                               const uint8_t *plaintext,\n+                               size_t plaintextLen);\n+\n+    extern BRCryptoBoolean\n+    cryptoCipherEncrypt (BRCryptoCipher cipher,\n+                         uint8_t *ciphertext,\n+                         size_t ciphertextLen,\n+                         const uint8_t *plaintext,\n+                         size_t plaintextLen);\n+\n+    extern size_t\n+    cryptoCipherDecryptLength (BRCryptoCipher cipher,\n+                               const char *ciphertext,\n+                               size_t ciphertextLen);\n+\n+    extern BRCryptoBoolean\n+    cryptoCipherDecrypt (BRCryptoCipher cipher,\n+                         uint8_t *plaintext,\n+                         size_t plaintextLen,\n+                         const char *ciphertext,\n+                         size_t ciphertextLen);\n+\n+    DECLARE_CRYPTO_GIVE_TAKE (BRCryptoCipher, cryptoCipher);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* BRCryptoCipher_h */\ndiff --git a/crypto/BRCryptoCoder.c b/crypto/BRCryptoCoder.c\nnew file mode 100644\nindex 00000000..958fc988\n--- /dev/null\n+++ b/crypto/BRCryptoCoder.c\n@@ -0,0 +1,198 @@\n+//\n+//  BRCryptoCoder.c\n+//  BRCore\n+//\n+//  Created by Michael Carrara on 9/23/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#include "BRCryptoCoder.h"\n+#include "ethereum/util/BRUtilHex.h"\n+#include "support/BRBase58.h"\n+\n+static void\n+cryptoCoderRelease (BRCryptoCoder coder);\n+\n+struct BRCryptoCoderRecord {\n+    BRCryptoCoderType type;\n+    BRCryptoRef ref;\n+};\n+\n+extern BRCryptoCoder\n+cryptoCoderCreate(BRCryptoCoderType type) {\n+    BRCryptoCoder coder = NULL;\n+\n+    switch (type) {\n+        case CRYPTO_CODER_HEX:\n+        case CRYPTO_CODER_BASE58:\n+        case CRYPTO_CODER_BASE58CHECK: {\n+            coder = calloc (1, sizeof(struct BRCryptoCoderRecord));\n+            coder->type = type;\n+            coder->ref = CRYPTO_REF_ASSIGN(cryptoCoderRelease);\n+            break;\n+        }\n+        default: {\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return coder;\n+}\n+\n+static void\n+cryptoCoderRelease (BRCryptoCoder coder) {\n+    memset (coder, 0, sizeof(*coder));\n+    free (coder);\n+}\n+\n+extern size_t\n+cryptoCoderEncodeLength (BRCryptoCoder coder,\n+                         const uint8_t *src,\n+                         size_t srcLen) {\n+    // - src CANNOT be NULL (see: BRBase58Encode), even with srcLen of 0\n+    if (NULL == src) {\n+        assert (0);\n+        return 0;\n+    }\n+\n+    size_t length = 0;\n+\n+    switch (coder->type) {\n+        case CRYPTO_CODER_HEX: {\n+            length = encodeHexLength (srcLen);\n+            break;\n+        }\n+        case CRYPTO_CODER_BASE58: {\n+            length = BRBase58Encode (NULL, 0, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_CODER_BASE58CHECK: {\n+            length = BRBase58CheckEncode (NULL, 0, src, srcLen);\n+            break;\n+        }\n+        default: {\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return length;\n+}\n+\n+extern BRCryptoBoolean\n+cryptoCoderEncode (BRCryptoCoder coder,\n+                   char *dst,\n+                   size_t dstLen,\n+                   const uint8_t *src,\n+                   size_t srcLen) {\n+    // - src CANNOT be NULL (see: BRBase58Encode), even with srcLen of 0\n+    // - dst MUST be non-NULL and sufficiently sized\n+    if (NULL == src ||\n+        NULL == dst || dstLen < cryptoCoderEncodeLength (coder, src, srcLen)) {\n+        assert (0);\n+        return CRYPTO_FALSE;\n+    }\n+\n+    BRCryptoBoolean result = CRYPTO_FALSE;\n+\n+    switch (coder->type) {\n+        case CRYPTO_CODER_HEX: {\n+            encodeHex (dst, dstLen, src, srcLen);\n+            result = CRYPTO_TRUE;\n+            break;\n+        }\n+        case CRYPTO_CODER_BASE58: {\n+            result = AS_CRYPTO_BOOLEAN (BRBase58Encode (dst, dstLen, src, srcLen));\n+            break;\n+        }\n+        case CRYPTO_CODER_BASE58CHECK: {\n+            result = AS_CRYPTO_BOOLEAN (BRBase58CheckEncode (dst, dstLen, src, srcLen));\n+            break;\n+        }\n+        default: {\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return result;\n+}\n+\n+extern size_t\n+cryptoCoderDecodeLength (BRCryptoCoder coder,\n+                         const char *src) {\n+    // - src CANNOT be NULL (see: BRBase58Decode), even with srcLen of 0\n+    if (NULL == src) {\n+        assert (0);\n+        return 0;\n+    }\n+\n+    size_t length = 0;\n+\n+    switch (coder->type) {\n+        case CRYPTO_CODER_HEX: {\n+            size_t strLen = strlen (src);\n+            length = (0 == strLen % 2) ? decodeHexLength (strLen) : 0;\n+            break;\n+        }\n+        case CRYPTO_CODER_BASE58: {\n+            length = BRBase58Decode (NULL, 0, src);\n+            break;\n+        }\n+        case CRYPTO_CODER_BASE58CHECK: {\n+            length = BRBase58CheckDecode (NULL, 0, src);\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return length;\n+}\n+\n+extern BRCryptoBoolean\n+cryptoCoderDecode (BRCryptoCoder coder,\n+                   uint8_t *dst,\n+                   size_t dstLen,\n+                   const char *src) {\n+    // - src CANNOT be NULL (see: BRBase58Decode), even with srcLen of 0\n+    // - dst MUST be non-NULL and sufficiently sized\n+    if (NULL == src ||\n+        NULL == dst || dstLen < cryptoCoderDecodeLength (coder, src)) {\n+        assert (0);\n+        return CRYPTO_FALSE;\n+    }\n+\n+    BRCryptoBoolean result = CRYPTO_FALSE;\n+\n+    switch (coder->type) {\n+        case CRYPTO_CODER_HEX: {\n+            decodeHex (dst, dstLen, src, strlen (src));\n+            result = CRYPTO_TRUE;\n+            break;\n+        }\n+        case CRYPTO_CODER_BASE58: {\n+            result = AS_CRYPTO_BOOLEAN (BRBase58Decode (dst, dstLen, src));\n+            break;\n+        }\n+        case CRYPTO_CODER_BASE58CHECK: {\n+            result = AS_CRYPTO_BOOLEAN (BRBase58CheckDecode (dst, dstLen, src));\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return result;\n+}\n+\n+IMPLEMENT_CRYPTO_GIVE_TAKE (BRCryptoCoder, cryptoCoder);\ndiff --git a/crypto/BRCryptoCoder.h b/crypto/BRCryptoCoder.h\nnew file mode 100644\nindex 00000000..4dc19e35\n--- /dev/null\n+++ b/crypto/BRCryptoCoder.h\n@@ -0,0 +1,59 @@\n+//\n+//  BRCryptoCoder.h\n+//  BRCore\n+//\n+//  Created by Michael Carrara on 9/23/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#ifndef BRCryptoCoder_h\n+#define BRCryptoCoder_h\n+\n+#include "BRCryptoBase.h"\n+\n+#ifdef __cplusplus\n+extern "C" {\n+#endif\n+\n+    typedef enum {\n+        CRYPTO_CODER_HEX,\n+        CRYPTO_CODER_BASE58,\n+        CRYPTO_CODER_BASE58CHECK\n+    } BRCryptoCoderType;\n+\n+    typedef struct BRCryptoCoderRecord *BRCryptoCoder;\n+\n+    extern BRCryptoCoder\n+    cryptoCoderCreate(BRCryptoCoderType type);\n+\n+    extern size_t\n+    cryptoCoderEncodeLength (BRCryptoCoder coder,\n+                             const uint8_t *src,\n+                             size_t srcLen);\n+\n+    extern BRCryptoBoolean\n+    cryptoCoderEncode (BRCryptoCoder coder,\n+                       char *dst,\n+                       size_t dstLen,\n+                       const uint8_t *src,\n+                       size_t srcLen);\n+\n+    extern size_t\n+    cryptoCoderDecodeLength (BRCryptoCoder coder,\n+                             const char *src);\n+\n+    extern BRCryptoBoolean\n+    cryptoCoderDecode (BRCryptoCoder coder,\n+                       uint8_t *dst,\n+                       size_t dstLen,\n+                       const char *src);\n+\n+    DECLARE_CRYPTO_GIVE_TAKE (BRCryptoCoder, cryptoCoder);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* BRCryptoCoder_h */\ndiff --git a/crypto/BRCryptoHasher.c b/crypto/BRCryptoHasher.c\nnew file mode 100644\nindex 00000000..33605280\n--- /dev/null\n+++ b/crypto/BRCryptoHasher.c\n@@ -0,0 +1,192 @@\n+//\n+//  BRCryptoHasher.c\n+//  BRCore\n+//\n+//  Created by Michael Carrara on 9/23/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#include "BRCryptoHasher.h"\n+#include "support/BRCrypto.h"\n+\n+static void\n+cryptoHasherRelease (BRCryptoHasher hasher);\n+\n+struct BRCryptoHasherRecord {\n+    BRCryptoHasherType type;\n+    BRCryptoRef ref;\n+};\n+\n+extern BRCryptoHasher\n+cryptoHasherCreate(BRCryptoHasherType type) {\n+    BRCryptoHasher hasher = NULL;\n+\n+    switch (type) {\n+        case CRYPTO_HASHER_SHA1:\n+        case CRYPTO_HASHER_SHA224:\n+        case CRYPTO_HASHER_SHA256:\n+        case CRYPTO_HASHER_SHA256_2:\n+        case CRYPTO_HASHER_SHA384:\n+        case CRYPTO_HASHER_SHA512:\n+        case CRYPTO_HASHER_SHA3:\n+        case CRYPTO_HASHER_RMD160:\n+        case CRYPTO_HASHER_HASH160:\n+        case CRYPTO_HASHER_KECCAK256:\n+        case CRYPTO_HASHER_MD5: {\n+            hasher = calloc (1, sizeof(struct BRCryptoHasherRecord));\n+            hasher->type = type;\n+            hasher->ref = CRYPTO_REF_ASSIGN(cryptoHasherRelease);\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return hasher;\n+}\n+\n+static void\n+cryptoHasherRelease (BRCryptoHasher hasher) {\n+    memset (hasher, 0, sizeof(*hasher));\n+    free (hasher);\n+}\n+\n+extern size_t\n+cryptoHasherLength (BRCryptoHasher hasher) {\n+    size_t length = 0;\n+\n+    switch (hasher->type) {\n+        case CRYPTO_HASHER_SHA1: {\n+            length = 20;\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA224: {\n+            length = 28;\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA256: {\n+            length = 32;\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA256_2: {\n+            length = 32;\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA384: {\n+            length = 48;\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA512: {\n+            length = 64;\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA3: {\n+            length = 32;\n+            break;\n+        }\n+        case CRYPTO_HASHER_RMD160: {\n+            length = 20;\n+            break;\n+        }\n+        case CRYPTO_HASHER_HASH160: {\n+            length = 20;\n+            break;\n+        }\n+        case CRYPTO_HASHER_KECCAK256: {\n+            length = 32;\n+            break;\n+        }\n+        case CRYPTO_HASHER_MD5: {\n+            length = 16;\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return length;\n+}\n+\n+extern BRCryptoBoolean\n+cryptoHasherHash (BRCryptoHasher hasher,\n+                  uint8_t *dst,\n+                  size_t dstLen,\n+                  const uint8_t *src,\n+                  size_t srcLen) {\n+    // - src CAN be NULL, if srcLen is 0\n+    // - dst MUST be non-NULL and sufficiently sized\n+    if ((NULL == src && 0 != srcLen) ||\n+        NULL == dst || dstLen < cryptoHasherLength (hasher)) {\n+        assert (0);\n+        return CRYPTO_FALSE;\n+    }\n+\n+    // the hash routines don\'t return anything so assume success\n+    // and only treat the unhandled case as failure\n+    BRCryptoBoolean result = CRYPTO_TRUE;\n+\n+    switch (hasher->type) {\n+        case CRYPTO_HASHER_SHA1: {\n+            BRSHA1 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA224: {\n+            BRSHA224 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA256: {\n+            BRSHA256 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA256_2: {\n+            BRSHA256_2 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA384: {\n+            BRSHA384 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA512: {\n+            BRSHA512 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_SHA3: {\n+            BRSHA3_256 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_RMD160: {\n+            BRRMD160 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_HASH160: {\n+            BRHash160 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_KECCAK256: {\n+            BRKeccak256 (dst, src, srcLen);\n+            break;\n+        }\n+        case CRYPTO_HASHER_MD5: {\n+            BRMD5 (dst, src, srcLen);\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            result = CRYPTO_FALSE;\n+            break;\n+        }\n+    }\n+\n+    return result;\n+}\n+\n+IMPLEMENT_CRYPTO_GIVE_TAKE (BRCryptoHasher, cryptoHasher);\ndiff --git a/crypto/BRCryptoHasher.h b/crypto/BRCryptoHasher.h\nnew file mode 100644\nindex 00000000..a7215e37\n--- /dev/null\n+++ b/crypto/BRCryptoHasher.h\n@@ -0,0 +1,55 @@\n+//\n+//  BRCryptoHasher.h\n+//  BRCore\n+//\n+//  Created by Michael Carrara on 9/23/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#ifndef BRCryptoHasher_h\n+#define BRCryptoHasher_h\n+\n+#include "BRCryptoBase.h"\n+\n+#ifdef __cplusplus\n+extern "C" {\n+#endif\n+\n+    typedef enum {\n+        CRYPTO_HASHER_SHA1,\n+        CRYPTO_HASHER_SHA224,\n+        CRYPTO_HASHER_SHA256,\n+        CRYPTO_HASHER_SHA256_2,\n+        CRYPTO_HASHER_SHA384,\n+        CRYPTO_HASHER_SHA512,\n+        CRYPTO_HASHER_SHA3,\n+        CRYPTO_HASHER_RMD160,\n+        CRYPTO_HASHER_HASH160,\n+        CRYPTO_HASHER_KECCAK256,\n+        CRYPTO_HASHER_MD5\n+    } BRCryptoHasherType;\n+\n+    typedef struct BRCryptoHasherRecord *BRCryptoHasher;\n+\n+    extern BRCryptoHasher\n+    cryptoHasherCreate(BRCryptoHasherType type);\n+\n+    extern size_t\n+    cryptoHasherLength (BRCryptoHasher hasher);\n+\n+    extern BRCryptoBoolean\n+    cryptoHasherHash (BRCryptoHasher hasher,\n+                      uint8_t *dst,\n+                      size_t dstLen,\n+                      const uint8_t *src,\n+                      size_t srcLen);\n+\n+    DECLARE_CRYPTO_GIVE_TAKE (BRCryptoHasher, cryptoHasher);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* BRCryptoHasher_h */\ndiff --git a/crypto/BRCryptoPrivate.h b/crypto/BRCryptoPrivate.h\nindex 103f5d93..37f87df7 100644\n--- a/crypto/BRCryptoPrivate.h\n+++ b/crypto/BRCryptoPrivate.h\n@@ -386,6 +386,10 @@ extern "C" {\n     cryptoWalletManagerSetState (BRCryptoWalletManager cwm,\n                                  BRCryptoWalletManagerState state);\n \n+\n+    private_extern void\n+    cryptoWalletManagerStop (BRCryptoWalletManager cwm);\n+\n     private_extern BRWalletManager\n     cryptoWalletManagerAsBTC (BRCryptoWalletManager manager);\n \ndiff --git a/crypto/BRCryptoSigner.c b/crypto/BRCryptoSigner.c\nnew file mode 100644\nindex 00000000..5fe0365a\n--- /dev/null\n+++ b/crypto/BRCryptoSigner.c\n@@ -0,0 +1,170 @@\n+//\n+//  BRCryptoSigner.c\n+//  BRCore\n+//\n+//  Created by Michael Carrara on 9/23/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#include "BRCryptoSigner.h"\n+#include "support/BRCrypto.h"\n+\n+static void\n+cryptoSignerRelease (BRCryptoSigner signer);\n+\n+struct BRCryptoSignerRecord {\n+    BRCryptoSignerType type;\n+    BRCryptoRef ref;\n+};\n+\n+extern BRCryptoSigner\n+cryptoSignerCreate(BRCryptoSignerType type) {\n+    BRCryptoSigner signer = NULL;\n+\n+    switch (type) {\n+        case CRYPTO_SIGNER_BASIC_DER:\n+        case CRYPTO_SIGNER_BASIC_JOSE:\n+        case CRYPTO_SIGNER_COMPACT: {\n+            signer = calloc (1, sizeof(struct BRCryptoSignerRecord));\n+            signer->type = type;\n+            signer->ref = CRYPTO_REF_ASSIGN(cryptoSignerRelease);\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return signer;\n+}\n+\n+static void\n+cryptoSignerRelease (BRCryptoSigner signer) {\n+    memset (signer, 0, sizeof(*signer));\n+    free (signer);\n+}\n+\n+extern size_t\n+cryptoSignerSignLength (BRCryptoSigner signer,\n+                        BRCryptoKey key,\n+                        const uint8_t *src,\n+                        size_t srcLen) {\n+    // - key CANNOT be NULL\n+    // - src CANNOT be NULL and must be 32 bytes long (i.e. a UINT256)\n+    if (NULL == key ||\n+        NULL == src || 32 != srcLen) {\n+        assert (0);\n+        return 0;\n+    }\n+\n+    size_t length = 0;\n+\n+    switch (signer->type) {\n+        case CRYPTO_SIGNER_BASIC_DER: {\n+            length = BRKeySign (cryptoKeyGetCore (key), NULL, 0, UInt256Get (src));\n+            break;\n+        }\n+        case CRYPTO_SIGNER_BASIC_JOSE: {\n+            length = BRKeySignJOSE (cryptoKeyGetCore (key), NULL, 0, UInt256Get (src));\n+            break;\n+        }\n+        case CRYPTO_SIGNER_COMPACT: {\n+            length = BRKeyCompactSign (cryptoKeyGetCore (key), NULL, 0, UInt256Get (src));\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return length;\n+}\n+\n+extern BRCryptoBoolean\n+cryptoSignerSign (BRCryptoSigner signer,\n+                  BRCryptoKey key,\n+                  uint8_t *dst,\n+                  size_t dstLen,\n+                  const uint8_t *src,\n+                  size_t srcLen) {\n+    // - key CANNOT be NULL\n+    // - src CANNOT be NULL and must be 32 bytes long (i.e. a UINT256)\n+    // - dst MUST be non-NULL and sufficiently sized\n+    if (NULL == key ||\n+        NULL == src || 32 != srcLen ||\n+        NULL == dst || dstLen < cryptoSignerSignLength (signer, key, src, srcLen)) {\n+        assert (0);\n+        return CRYPTO_FALSE;\n+    }\n+\n+    BRCryptoBoolean result = CRYPTO_FALSE;\n+\n+    switch (signer->type) {\n+        case CRYPTO_SIGNER_BASIC_DER: {\n+            result = AS_CRYPTO_BOOLEAN (BRKeySign (cryptoKeyGetCore (key), dst, dstLen, UInt256Get (src)));\n+            break;\n+        }\n+        case CRYPTO_SIGNER_BASIC_JOSE: {\n+            result = AS_CRYPTO_BOOLEAN (BRKeySignJOSE (cryptoKeyGetCore (key), dst, dstLen, UInt256Get (src)));\n+            break;\n+        }\n+        case CRYPTO_SIGNER_COMPACT: {\n+            result = AS_CRYPTO_BOOLEAN (BRKeyCompactSign (cryptoKeyGetCore (key), dst, dstLen, UInt256Get (src)));\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return result;\n+}\n+\n+extern BRCryptoKey\n+cryptoSignerRecover (BRCryptoSigner signer,\n+                     const uint8_t *digest,\n+                     size_t digestLen,\n+                     const uint8_t *signature,\n+                     size_t signatureLen) {\n+    // - digest CANNOT be NULL and must be 32 bytes long (i.e. a UINT256)\n+    // - signature CANNOT be NULL (size checked independently in recovery methods)\n+    if (NULL == digest || 32 != digestLen || NULL == signature) {\n+        assert (0);\n+        return NULL;\n+    }\n+\n+    BRCryptoKey key = NULL;\n+\n+    switch (signer->type) {\n+        case CRYPTO_SIGNER_BASIC_DER:\n+        case CRYPTO_SIGNER_BASIC_JOSE: {\n+            // not supported, but not necessarily worth an assert\n+            break;\n+        }\n+        case CRYPTO_SIGNER_COMPACT: {\n+            BRKey k;\n+            if (1 == BRKeyRecoverPubKey (&k, UInt256Get (digest), signature, signatureLen) ) {\n+                key = cryptoKeyCreateFromKey (&k);\n+            }\n+            BRKeyClean (&k);\n+            break;\n+        }\n+        default: {\n+            // for an unsupported algorithm, assert\n+            assert (0);\n+            break;\n+        }\n+    }\n+\n+    return key;\n+}\n+\n+IMPLEMENT_CRYPTO_GIVE_TAKE (BRCryptoSigner, cryptoSigner);\ndiff --git a/crypto/BRCryptoSigner.h b/crypto/BRCryptoSigner.h\nnew file mode 100644\nindex 00000000..0242697c\n--- /dev/null\n+++ b/crypto/BRCryptoSigner.h\n@@ -0,0 +1,59 @@\n+//\n+//  BRCryptoSigner.h\n+//  BRCore\n+//\n+//  Created by Michael Carrara on 9/23/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#ifndef BRCryptoSigner_h\n+#define BRCryptoSigner_h\n+\n+#include "BRCryptoBase.h"\n+#include "BRCryptoKey.h"\n+\n+#ifdef __cplusplus\n+extern "C" {\n+#endif\n+\n+    typedef enum {\n+        CRYPTO_SIGNER_BASIC_DER,\n+        CRYPTO_SIGNER_BASIC_JOSE,\n+        CRYPTO_SIGNER_COMPACT\n+    } BRCryptoSignerType;\n+\n+    typedef struct BRCryptoSignerRecord *BRCryptoSigner;\n+\n+    extern BRCryptoSigner\n+    cryptoSignerCreate(BRCryptoSignerType type);\n+\n+    extern size_t\n+    cryptoSignerSignLength (BRCryptoSigner signer,\n+                            BRCryptoKey key,\n+                            const uint8_t *src,\n+                            size_t srcLen);\n+\n+    extern BRCryptoBoolean\n+    cryptoSignerSign (BRCryptoSigner signer,\n+                      BRCryptoKey key,\n+                      uint8_t *dst,\n+                      size_t dstLen,\n+                      const uint8_t *src,\n+                      size_t srcLen);\n+\n+    extern BRCryptoKey\n+    cryptoSignerRecover (BRCryptoSigner signer,\n+                         const uint8_t *digest,\n+                         size_t digestLen,\n+                         const uint8_t *signature,\n+                         size_t signatureLen);\n+\n+    DECLARE_CRYPTO_GIVE_TAKE (BRCryptoSigner, cryptoSigner);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* BRCryptoSigner_h */\ndiff --git a/crypto/BRCryptoTransfer.c b/crypto/BRCryptoTransfer.c\nindex aaf3ff7b..5f064314 100644\n--- a/crypto/BRCryptoTransfer.c\n+++ b/crypto/BRCryptoTransfer.c\n@@ -129,14 +129,18 @@ cryptoTransferCreateAsBTC (BRCryptoUnit unit,\n \n         for (size_t index = 0; index < inputsCount; index++) {\n             size_t addressSize = BRTxInputAddress (&inputs[index], NULL, 0, addressParams);\n-            char address [addressSize];\n \n-            BRTxInputAddress (&inputs[index], address, addressSize, addressParams);\n+            // ensure address fits in a BRAddress struct, which adds a nul-terminator\n+            assert (addressSize < sizeof (BRAddress));\n+            if (0 != addressSize && addressSize < sizeof (BRAddress)) {\n+                char address [addressSize + 1];\n+                BRTxInputAddress (&inputs[index], address, addressSize, addressParams);\n+                address [addressSize] = \'\\0\'; // ensure address is nul-terminated\n \n-            if (inputsContain == BRWalletContainsAddress(wid, address)) {\n-                assert (addressSize < sizeof (BRAddress));\n-                transfer->sourceAddress = cryptoAddressCreateAsBTC (BRAddressFill (addressParams, address), isBTC);\n-                break;\n+                if (inputsContain == BRWalletContainsAddress(wid, address)) {\n+                    transfer->sourceAddress = cryptoAddressCreateAsBTC (BRAddressFill (addressParams, address), isBTC);\n+                    break;\n+                }\n             }\n         }\n     }\n@@ -149,17 +153,21 @@ cryptoTransferCreateAsBTC (BRCryptoUnit unit,\n \n         for (size_t index = 0; index < outputsCount; index++) {\n             size_t addressSize = BRTxOutputAddress (&outputs[index], NULL, 0, addressParams);\n-            char address [addressSize];\n-\n-            BRTxOutputAddress (&outputs[index], address, addressSize, addressParams);\n \n-            // There will be no targetAddress if we send the amount to ourselves.  In that\n-            // case `outputsContain = 0` and every output is our own address and thus 1 is always\n-            // returned by `BRWalletContainsAddress()`\n-            if (outputsContain == BRWalletContainsAddress(wid, address)) {\n-                assert (addressSize < sizeof (BRAddress));\n-                transfer->targetAddress = cryptoAddressCreateAsBTC (BRAddressFill (addressParams, address), isBTC);\n-                break;\n+            // ensure address fits in a BRAddress struct, which adds a nul-terminator\n+            assert (addressSize < sizeof (BRAddress));\n+            if (0 != addressSize && addressSize < sizeof (BRAddress)) {\n+                // There will be no targetAddress if we send the amount to ourselves.  In that\n+                // case `outputsContain = 0` and every output is our own address and thus 1 is always\n+                // returned by `BRWalletContainsAddress()`\n+                char address [addressSize + 1];\n+                BRTxOutputAddress (&outputs[index], address, addressSize, addressParams);\n+                address [addressSize] = \'\\0\'; // ensure address is nul-terminated\n+\n+                if (outputsContain == BRWalletContainsAddress(wid, address)) {\n+                    transfer->targetAddress = cryptoAddressCreateAsBTC (BRAddressFill (addressParams, address), isBTC);\n+                    break;\n+                }\n             }\n         }\n     }\n@@ -536,7 +544,7 @@ cryptoTransferGetDirection (BRCryptoTransfer transfer) {\n             int accountIsTarget = gwmAddressEqual (gwm, target, address);\n \n             // TODO: BRGenericAddress - release source, target, address\n-            \n+\n             if      ( accountIsSource &&  accountIsTarget) return CRYPTO_TRANSFER_RECOVERED;\n             else if ( accountIsSource && !accountIsTarget) return CRYPTO_TRANSFER_SENT;\n             else if (!accountIsSource &&  accountIsTarget) return CRYPTO_TRANSFER_RECEIVED;\ndiff --git a/crypto/BRCryptoWalletManager.c b/crypto/BRCryptoWalletManager.c\nindex 14f65c85..bc426523 100644\n--- a/crypto/BRCryptoWalletManager.c\n+++ b/crypto/BRCryptoWalletManager.c\n@@ -279,6 +279,10 @@ cryptoWalletManagerCreate (BRCryptoCWMListener listener,\n \n static void\n cryptoWalletManagerRelease (BRCryptoWalletManager cwm) {\n+    // Ensure CWM is stopped...\n+    cryptoWalletManagerStop (cwm);\n+\n+    // ... then release memory.\n     cryptoAccountGive (cwm->account);\n     cryptoNetworkGive (cwm->network);\n     if (NULL != cwm->wallet) cryptoWalletGive (cwm->wallet);\n@@ -289,7 +293,6 @@ cryptoWalletManagerRelease (BRCryptoWalletManager cwm) {\n \n     switch (cwm->type) {\n         case BLOCK_CHAIN_TYPE_BTC:\n-            BRWalletManagerStop (cwm->u.btc);\n             BRWalletManagerFree (cwm->u.btc);\n             break;\n         case BLOCK_CHAIN_TYPE_ETH:\n@@ -308,6 +311,21 @@ cryptoWalletManagerRelease (BRCryptoWalletManager cwm) {\n     free (cwm);\n }\n \n+private_extern void\n+cryptoWalletManagerStop (BRCryptoWalletManager cwm) {\n+    switch (cwm->type) {\n+        case BLOCK_CHAIN_TYPE_BTC:\n+            BRWalletManagerStop (cwm->u.btc);\n+            break;\n+        case BLOCK_CHAIN_TYPE_ETH:\n+            ewmStop (cwm->u.eth);\n+            break;\n+        case BLOCK_CHAIN_TYPE_GEN:\n+            gwmStop (cwm->u.gen);\n+            break;\n+    }\n+}\n+\n extern BRCryptoNetwork\n cryptoWalletManagerGetNetwork (BRCryptoWalletManager cwm) {\n     return cryptoNetworkTake (cwm->network);\n@@ -546,14 +564,15 @@ cryptoWalletManagerInstallETHTokensForCurrencies (BRCryptoWalletManager cwm) {\n                         // Argubably EWM should create a wallet for the token.  But, it doesn\'t.\n                         // So we\'ll call `ewmGetWalletHoldingToken()` to get a wallet.\n \n-                        ewmCreateToken (cwm->u.eth,\n-                                        address,\n-                                        cryptoCurrencyGetCode (c),\n-                                        cryptoCurrencyGetName(c),\n-                                        cryptoCurrencyGetUids(c), // description\n-                                        cryptoUnitGetBaseDecimalOffset(unitDefault),\n-                                        ethGasLimit,\n-                                        ethGasPrice);\n+                        BREthereumToken token = ewmCreateToken (cwm->u.eth,\n+                                                                address,\n+                                                                cryptoCurrencyGetCode (c),\n+                                                                cryptoCurrencyGetName(c),\n+                                                                cryptoCurrencyGetUids(c), // description\n+                                                                cryptoUnitGetBaseDecimalOffset(unitDefault),\n+                                                                ethGasLimit,\n+                                                                ethGasPrice);\n+                        assert (NULL != token); (void) &token;\n                     }\n                     break;\n                 }\ndiff --git a/ethereum/ewm/BREthereumEWM.c b/ethereum/ewm/BREthereumEWM.c\nindex 92eb3f07..c0e06d08 100644\n--- a/ethereum/ewm/BREthereumEWM.c\n+++ b/ethereum/ewm/BREthereumEWM.c\n@@ -52,60 +52,10 @@ ewmPeriodicDispatcher (BREventHandler handler,\n \n /// MARK: - Transaction File Service\n \n-static const char *fileServiceTypeTransactions = "transactions";\n-\n-enum {\n-    EWM_TRANSACTION_VERSION_1\n-};\n-\n-static UInt256\n-fileServiceTypeTransactionV1Identifier (BRFileServiceContext context,\n-                                        BRFileService fs,\n-                                        const void *entity) {\n-    BREthereumTransaction transaction = (BREthereumTransaction) entity;\n-    BREthereumHash hash = transactionGetHash(transaction);\n-\n-    UInt256 result;\n-    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n-    return result;\n-}\n-\n-static uint8_t *\n-fileServiceTypeTransactionV1Writer (BRFileServiceContext context,\n-                                    BRFileService fs,\n-                                    const void* entity,\n-                                    uint32_t *bytesCount) {\n-    BREthereumEWM ewm = context;\n-    BREthereumTransaction transaction = (BREthereumTransaction) entity;\n-\n-    BRRlpItem item = transactionRlpEncode(transaction, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n-    BRRlpData data = rlpGetData (ewm->coder, item);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    *bytesCount = (uint32_t) data.bytesCount;\n-    return data.bytes;\n-}\n-\n-static void *\n-fileServiceTypeTransactionV1Reader (BRFileServiceContext context,\n-                                    BRFileService fs,\n-                                    uint8_t *bytes,\n-                                    uint32_t bytesCount) {\n-    BREthereumEWM ewm = context;\n-\n-    BRRlpData data = { bytesCount, bytes };\n-    BRRlpItem item = rlpGetItem (ewm->coder, data);\n-\n-    BREthereumTransaction transaction = transactionRlpDecode(item, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    return transaction;\n-}\n-\n static BRSetOf(BREthereumTransaction)\n initialTransactionsLoad (BREthereumEWM ewm) {\n     BRSetOf(BREthereumTransaction) transactions = BRSetNew(transactionHashValue, transactionHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT);\n-    if (NULL != transactions && 1 != fileServiceLoad (ewm->fs, transactions, fileServiceTypeTransactions, 1)) {\n+    if (NULL != transactions && 1 != fileServiceLoad (ewm->fs, transactions, ewmFileServiceTypeTransactions, 1)) {\n         BRSetFreeAll (transactions, (void (*) (void*)) transactionRelease);\n         return NULL;\n     }\n@@ -114,60 +64,10 @@ initialTransactionsLoad (BREthereumEWM ewm) {\n \n /// MARK: - Log File Service\n \n-static const char *fileServiceTypeLogs = "logs";\n-\n-enum {\n-    EWM_LOG_VERSION_1\n-};\n-\n-static UInt256\n-fileServiceTypeLogV1Identifier (BRFileServiceContext context,\n-                                BRFileService fs,\n-                                const void *entity) {\n-    const BREthereumLog log = (BREthereumLog) entity;\n-    BREthereumHash hash = logGetHash( log);\n-\n-    UInt256 result;\n-    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n-    return result;\n-}\n-\n-static uint8_t *\n-fileServiceTypeLogV1Writer (BRFileServiceContext context,\n-                            BRFileService fs,\n-                            const void* entity,\n-                            uint32_t *bytesCount) {\n-    BREthereumEWM ewm = context;\n-    BREthereumLog log = (BREthereumLog) entity;\n-\n-    BRRlpItem item = logRlpEncode (log, RLP_TYPE_ARCHIVE, ewm->coder);\n-    BRRlpData data = rlpGetData (ewm->coder, item);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    *bytesCount = (uint32_t) data.bytesCount;\n-    return data.bytes;\n-}\n-\n-static void *\n-fileServiceTypeLogV1Reader (BRFileServiceContext context,\n-                            BRFileService fs,\n-                            uint8_t *bytes,\n-                            uint32_t bytesCount) {\n-    BREthereumEWM ewm = context;\n-\n-    BRRlpData data = { bytesCount, bytes };\n-    BRRlpItem item = rlpGetItem (ewm->coder, data);\n-\n-    BREthereumLog log = logRlpDecode(item, RLP_TYPE_ARCHIVE, ewm->coder);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    return log;\n-}\n-\n static BRSetOf(BREthereumLog)\n initialLogsLoad (BREthereumEWM ewm) {\n     BRSetOf(BREthereumLog) logs = BRSetNew(logHashValue, logHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT);\n-    if (NULL != logs && 1 != fileServiceLoad (ewm->fs, logs, fileServiceTypeLogs, 1)) {\n+    if (NULL != logs && 1 != fileServiceLoad (ewm->fs, logs, ewmFileServiceTypeLogs, 1)) {\n         BRSetFreeAll (logs, (void (*) (void*)) logRelease);\n         return NULL;\n     }\n@@ -175,191 +75,46 @@ initialLogsLoad (BREthereumEWM ewm) {\n }\n \n \n-/// MARK: - Block File Service\n-\n-static const char *fileServiceTypeBlocks = "blocks";\n-enum {\n-    EWM_BLOCK_VERSION_1\n-};\n-\n-static UInt256\n-fileServiceTypeBlockV1Identifier (BRFileServiceContext context,\n-                                  BRFileService fs,\n-                                  const void *entity) {\n-    const BREthereumBlock block = (BREthereumBlock) entity;\n-    BREthereumHash hash = blockGetHash(block);\n-\n-    UInt256 result;\n-    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n-    return result;\n-}\n-\n-static uint8_t *\n-fileServiceTypeBlockV1Writer (BRFileServiceContext context,\n-                              BRFileService fs,\n-                              const void* entity,\n-                              uint32_t *bytesCount) {\n-    BREthereumEWM ewm = context;\n-    BREthereumBlock block = (BREthereumBlock) entity;\n-\n-    BRRlpItem item = blockRlpEncode(block, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n-    BRRlpData data = rlpGetData (ewm->coder, item);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    *bytesCount = (uint32_t) data.bytesCount;\n-    return data.bytes;\n-}\n-\n-static void *\n-fileServiceTypeBlockV1Reader (BRFileServiceContext context,\n-                              BRFileService fs,\n-                              uint8_t *bytes,\n-                              uint32_t bytesCount) {\n-    BREthereumEWM ewm = context;\n-\n-    BRRlpData data = { bytesCount, bytes };\n-    BRRlpItem item = rlpGetItem (ewm->coder, data);\n-\n-    BREthereumBlock block = blockRlpDecode (item, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    return block;\n-}\n-\n static BRSetOf(BREthereumBlock)\n initialBlocksLoad (BREthereumEWM ewm) {\n     BRSetOf(BREthereumBlock) blocks = BRSetNew(blockHashValue, blockHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT);\n-    if (NULL != blocks && 1 != fileServiceLoad (ewm->fs, blocks, fileServiceTypeBlocks, 1)) {\n+    if (NULL != blocks && 1 != fileServiceLoad (ewm->fs, blocks, ewmFileServiceTypeBlocks, 1)) {\n         BRSetFreeAll (blocks,  (void (*) (void*)) blockRelease);\n         return NULL;\n     }\n     return blocks;\n }\n \n-/// MARK: - Node File Service\n-\n-static const char *fileServiceTypeNodes = "nodes";\n-enum {\n-    EWM_NODE_VERSION_1\n-};\n-\n-static UInt256\n-fileServiceTypeNodeV1Identifier (BRFileServiceContext context,\n-                                 BRFileService fs,\n-                                 const void *entity) {\n-    const BREthereumNodeConfig node = (BREthereumNodeConfig) entity;\n-\n-    BREthereumHash hash = nodeConfigGetHash(node);\n-\n-    UInt256 result;\n-    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n-    return result;\n-}\n-\n-static uint8_t *\n-fileServiceTypeNodeV1Writer (BRFileServiceContext context,\n-                             BRFileService fs,\n-                             const void* entity,\n-                             uint32_t *bytesCount) {\n-    BREthereumEWM ewm = context;\n-    const BREthereumNodeConfig node = (BREthereumNodeConfig) entity;\n-\n-    BRRlpItem item = nodeConfigEncode (node, ewm->coder);\n-    BRRlpData data = rlpGetData (ewm->coder, item);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    *bytesCount = (uint32_t) data.bytesCount;\n-    return data.bytes;\n-}\n-\n-static void *\n-fileServiceTypeNodeV1Reader (BRFileServiceContext context,\n-                             BRFileService fs,\n-                             uint8_t *bytes,\n-                             uint32_t bytesCount) {\n-    BREthereumEWM ewm = context;\n-\n-    BRRlpData data = { bytesCount, bytes };\n-    BRRlpItem item = rlpGetItem (ewm->coder, data);\n-\n-    BREthereumNodeConfig node = nodeConfigDecode (item, ewm->coder);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    return node;\n-}\n-\n static BRSetOf(BREthereumNodeConfig)\n initialNodesLoad (BREthereumEWM ewm) {\n     BRSetOf(BREthereumNodeConfig) nodes = BRSetNew(nodeConfigHashValue, nodeConfigHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT);\n-    if (NULL != nodes && 1 != fileServiceLoad (ewm->fs, nodes, fileServiceTypeNodes, 1)) {\n+    if (NULL != nodes && 1 != fileServiceLoad (ewm->fs, nodes, ewmFileServiceTypeNodes, 1)) {\n         BRSetFreeAll (nodes, (void (*) (void*)) nodeConfigRelease);\n         return NULL;\n     }\n     return nodes;\n }\n \n-/// MARK: - Token File Service\n-\n-static const char *fileServiceTypeTokens = "tokens";\n-\n-enum {\n-    EWM_TOKEN_VERSION_1\n-};\n-\n-static UInt256\n-fileServiceTypeTokenV1Identifier (BRFileServiceContext context,\n-                                        BRFileService fs,\n-                                        const void *entity) {\n-    BREthereumToken token = (BREthereumToken) entity;\n-    BREthereumHash hash = tokenGetHash(token);\n-\n-    UInt256 result;\n-    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n-    return result;\n-}\n-\n-static uint8_t *\n-fileServiceTypeTokenV1Writer (BRFileServiceContext context,\n-                                    BRFileService fs,\n-                                    const void* entity,\n-                                    uint32_t *bytesCount) {\n-    BREthereumEWM ewm = context;\n-    BREthereumToken token = (BREthereumToken) entity;\n-\n-    BRRlpItem item = tokenEncode(token, ewm->coder);\n-    BRRlpData data = rlpGetData (ewm->coder, item);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    *bytesCount = (uint32_t) data.bytesCount;\n-    return data.bytes;\n-}\n-\n-static void *\n-fileServiceTypeTokenV1Reader (BRFileServiceContext context,\n-                                    BRFileService fs,\n-                                    uint8_t *bytes,\n-                                    uint32_t bytesCount) {\n-    BREthereumEWM ewm = context;\n-\n-    BRRlpData data = { bytesCount, bytes };\n-    BRRlpItem item = rlpGetItem (ewm->coder, data);\n-\n-    BREthereumToken token = tokenDecode(item, ewm->coder);\n-    rlpReleaseItem (ewm->coder, item);\n-\n-    return token;\n-}\n-\n static BRSetOf(BREthereumToken)\n initialTokensLoad (BREthereumEWM ewm) {\n     BRSetOf(BREthereumToken) tokens = tokenSetCreate (EWM_INITIAL_SET_SIZE_DEFAULT);\n-    if (NULL != tokens && 1 != fileServiceLoad (ewm->fs, tokens, fileServiceTypeTokens, 1)) {\n+    if (NULL != tokens && 1 != fileServiceLoad (ewm->fs, tokens, ewmFileServiceTypeTokens, 1)) {\n         BRSetFreeAll (tokens, (void (*) (void*)) tokenRelease);\n         return NULL;\n     }\n     return tokens;\n }\n \n+static BRSetOf(BREthereumWalletState)\n+initialWalletsLoad (BREthereumEWM ewm) {\n+    BRSetOf(BREthereumWalletState) states = walletStateSetCreate (EWM_INITIAL_SET_SIZE_DEFAULT);\n+    if (NULL != states && 1 != fileServiceLoad (ewm->fs, states, ewmFileServiceTypeWallets, 1)) {\n+        BRSetFreeAll (states, (void (*) (void*)) walletStateRelease);\n+        return NULL;\n+    }\n+    return states;\n+}\n+\n \n /**\n  *\n@@ -389,7 +144,9 @@ ewmFileServiceErrorHandler (BRFileServiceContext context,\n                      error.u.entity.reason);\n             break;\n         case FILE_SERVICE_SDB:\n-            eth_log ("EWM", "FileService Error: SDB: %d", error.u.sdb.code);\n+            eth_log ("EWM", "FileService Error: SDB: (%d): %s",\n+                     error.u.sdb.code,\n+                     error.u.sdb.reason);\n             break;\n     }\n     eth_log ("EWM", "FileService Error: FORCED SYNC%s", "");\n@@ -436,19 +193,21 @@ ewmCreateInitialSets (BREthereumEWM ewm,\n                       BRSetOf(BREthereumLog) *logs,\n                       BRSetOf(BREthereumNodeConfig) *nodes,\n                       BRSetOf(BREthereumBlock) *blocks,\n-                      BRSetOf(BRethereumToken) *tokens) {\n+                      BRSetOf(BREthereumToken) *tokens,\n+                      BRSetOf(BREthereumWalletState) *states) {\n \n     *transactions = initialTransactionsLoad(ewm);\n     *logs = initialLogsLoad(ewm);\n     *nodes = initialNodesLoad(ewm);\n     *blocks = initialBlocksLoad(ewm);\n     *tokens = initialTokensLoad(ewm);\n+    *states = initialWalletsLoad(ewm);\n \n     // If any are NULL, then we have an error and a full sync is required.  The sync will be\n     // started automatically, as part of the normal processing, of \'blocks\' (we\'ll use a checkpoint,\n     // before the `accountTimestamp, which will be well in the past and we\'ll sync up to the\n     // head of the blockchain).\n-    if (NULL == *transactions || NULL == *logs || NULL == *nodes || NULL == *blocks || NULL == *tokens) {\n+    if (NULL == *transactions || NULL == *logs || NULL == *nodes || NULL == *blocks || NULL == *tokens || NULL == *states) {\n         // If the set exists, clear it out completely and then create another one.  Note, since\n         // we have `BRSetFreeAll()` we\'ll use that even though it frees the set and then we\n         // create one again, minimally wastefully.\n@@ -466,6 +225,9 @@ ewmCreateInitialSets (BREthereumEWM ewm,\n \n         if (NULL != *tokens) { BRSetFreeAll (*tokens, (void (*) (void*)) tokenRelease); }\n         *tokens = tokenSetCreate (EWM_INITIAL_SET_SIZE_DEFAULT);\n+\n+        if (NULL != *states) { BRSetFreeAll (*states, (void (*) (void*)) walletStateRelease); }\n+        *states = walletStateSetCreate(EWM_INITIAL_SET_SIZE_DEFAULT);\n     }\n \n     // If we have no blocks; then add a checkpoint\n@@ -530,71 +292,23 @@ ewmCreate (BREthereumNetwork network,\n \n     // The file service.  Initialize {nodes, blocks, transactions and logs} from the FileService\n \n-    ewm->fs = fileServiceCreate (storagePath, "eth", networkGetName(network),\n-                                 ewm,\n-                                 ewmFileServiceErrorHandler);\n+    ewm->fs = fileServiceCreateFromTypeSpecfications (storagePath, "eth", networkGetName(network),\n+                                                      ewm,\n+                                                      ewmFileServiceErrorHandler,\n+                                                      ewmFileServiceSpecificationsCount,\n+                                                      ewmFileServiceSpecifications);\n     if (NULL == ewm->fs) return ewmCreateErrorHandler(ewm, 1, "create");\n \n-    /// Transaction\n-    if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeTransactions, EWM_TRANSACTION_VERSION_1,\n-                                    (BRFileServiceContext) ewm,\n-                                    fileServiceTypeTransactionV1Identifier,\n-                                    fileServiceTypeTransactionV1Reader,\n-                                    fileServiceTypeTransactionV1Writer) ||\n-        1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeTransactions,\n-                                              EWM_TRANSACTION_VERSION_1))\n-        return ewmCreateErrorHandler(ewm, 1, fileServiceTypeTransactions);\n-\n-    /// Log\n-    if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeLogs, EWM_LOG_VERSION_1,\n-                                    (BRFileServiceContext) ewm,\n-                                    fileServiceTypeLogV1Identifier,\n-                                    fileServiceTypeLogV1Reader,\n-                                    fileServiceTypeLogV1Writer) ||\n-        1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeLogs,\n-                                              EWM_LOG_VERSION_1))\n-        return ewmCreateErrorHandler(ewm, 1, fileServiceTypeLogs);\n-\n-    /// Peer\n-    if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeNodes, EWM_NODE_VERSION_1,\n-                                    (BRFileServiceContext) ewm,\n-                                    fileServiceTypeNodeV1Identifier,\n-                                    fileServiceTypeNodeV1Reader,\n-                                    fileServiceTypeNodeV1Writer) ||\n-        1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeNodes,\n-                                              EWM_NODE_VERSION_1))\n-        return ewmCreateErrorHandler(ewm, 1, fileServiceTypeNodes);\n-\n-\n-   /// Block\n-    if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeBlocks, EWM_BLOCK_VERSION_1,\n-                                    (BRFileServiceContext) ewm,\n-                                    fileServiceTypeBlockV1Identifier,\n-                                    fileServiceTypeBlockV1Reader,\n-                                    fileServiceTypeBlockV1Writer) ||\n-        1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeBlocks,\n-                                              EWM_BLOCK_VERSION_1))\n-        return ewmCreateErrorHandler(ewm, 1, fileServiceTypeBlocks);\n-\n-    /// Token\n-    if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeTokens, EWM_TOKEN_VERSION_1,\n-                                    (BRFileServiceContext) ewm,\n-                                    fileServiceTypeTokenV1Identifier,\n-                                    fileServiceTypeTokenV1Reader,\n-                                    fileServiceTypeTokenV1Writer) ||\n-        1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeTokens,\n-                                              EWM_TOKEN_VERSION_1))\n-        return ewmCreateErrorHandler(ewm, 1, fileServiceTypeTokens);\n-\n-\n     // Load all the persistent entities\n     BRSetOf(BREthereumTransaction) transactions;\n     BRSetOf(BREthereumLog) logs;\n     BRSetOf(BREthereumNodeConfig) nodes;\n     BRSetOf(BREthereumBlock) blocks;\n     BRSetOf(BREthereumToken) tokens;\n+    BRSetOf(BREthereumWalletState) walletStates;\n \n-    ewmCreateInitialSets (ewm, ewm->network, ewm->accountTimestamp, &transactions, &logs, &nodes, &blocks, &tokens);\n+    ewmCreateInitialSets (ewm, ewm->network, ewm->accountTimestamp,\n+                          &transactions, &logs, &nodes, &blocks, &tokens, &walletStates);\n \n     // Save the recovered tokens\n     ewm->tokens = tokens;\n@@ -628,6 +342,38 @@ ewmCreate (BREthereumNetwork network,\n     BRAssertDefineRecovery ((BRAssertRecoveryInfo) ewm,\n                             (BRAssertRecoveryHandler) ewmAssertRecovery);\n \n+    // Having restored the WalletState. restore that Wallet (balance).\n+    FOR_SET (BREthereumWalletState, state, walletStates) {\n+        BREthereumAddress address = walletStateGetAddress (state);\n+\n+        // If the WalletState address is EMPTY_ADDRESS_INIT, then the state is for ETHER\n+        BREthereumBoolean addressIsForEther = addressEqual (EMPTY_ADDRESS_INIT, address);\n+\n+        // See if we have a token.\n+        BREthereumToken token = (ETHEREUM_BOOLEAN_IS_TRUE (addressIsForEther)\n+                                 ? NULL\n+                                 : ewmLookupToken (ewm, address));\n+\n+        // If we don\'t have a token and the address is not for ether, then ignore the state\n+        // This would occur if we have a state for a token that is no longer supported.\n+        if (NULL == token && ETHEREUM_BOOLEAN_IS_FALSE(addressIsForEther)) continue;\n+\n+         // Get the balance\n+        BREthereumAmount balance = (NULL == token\n+                                    ? amountCreateEther (etherCreate (walletStateGetAmount (state)))\n+                                    : amountCreateToken (createTokenQuantity (token, walletStateGetAmount (state))));\n+\n+        // Finally, update the balance; this will create TOK wallets as required.\n+        ewmHandleBalance (ewm, balance);\n+\n+        if (NULL == token) {\n+            accountSetAddressNonce (ewm->account,\n+                                    accountGetPrimaryAddress(ewm->account),\n+                                    walletStateGetNonce(state),\n+                                    ETHEREUM_BOOLEAN_TRUE);\n+        }\n+    }\n+\n     // Create BCS - note: when BCS processes blocks, peers, transactions, and logs there\n     // will be callbacks made to the EWM client.  Because we\'ve defined `handlerForMain`\n     // any callbacks will be queued and then handled when EWM actually starts\n@@ -672,17 +418,13 @@ ewmCreate (BREthereumNetwork network,\n             // how to get an ERC20 balance (execute a (free) transaction for \'balance\'??); this\n             // later case applies for `bcsCreate()` below in P2P modes.\n             //\n-            // But, on startup we have the transactions and the logs and DO NOT persistently store\n-            // the per-wallet balance.  Thus, to get the balance we need an API hit (or a P2P hit).\n-            // We might not have network...  So, get the balance assigned from existing transfers.\n+            // In addition, because we DO NOT handle \'internal transactions\' the sum of transfers\n+            // cannot reliably produce the account balance.\n             //\n-            // Note: at this point transfers are not in wallets - we\'ve just signalled adding the\n-            // transactions and logs.  If we are going to update the balance (and it won\'t be\n-            // N^2 as each transfer is added), then we need to signal a wallet balance update.\n-            //\n-            // One.Event.to.Update.Them.All\n-            ewmSignalUpdateWalletBalances(ewm);\n+            // So, we have all the logs and transactions and we won\'t modify the wallet balances\n+            // that have been restored from persistent state.\n \n+#if 0\n             // ... and then the latest block.\n             BREthereumBlock lastBlock = NULL;\n             FOR_SET (BREthereumBlock, block, blocks)\n@@ -692,6 +434,23 @@ ewmCreate (BREthereumNetwork network,\n                                  blockGetHash( lastBlock),\n                                  blockGetNumber (lastBlock),\n                                  blockGetTimestamp (lastBlock));\n+#endif\n+            // Use the provided `blockHeight` in API modes.  We only have the `blockHeight` as a\n+            // parameter, thus calling `ewmSignalBlockChain()` with \'empty\' values for `blockHash`\n+            // and `blockTimestamp` works because we know that `ewmHandleBlockChain` doesn\'t use\n+            // those parameters.\n+            //\n+            // What was the expectation of the above `lastBlock` code in API modes?  It might have\n+            // been from a time when `blockHeight` was not passed in as a parameters and thus we\n+            // need some block height and the best we could do was the saved blocks (like from a\n+            // P2P mode) or the checkpoint which is always there, at least.\n+            //\n+            // Our current API appraoch is to use BlockSet where a) we have the current block height\n+            // or b) if BlockSet, is down we hardcode the block height at the time of the software\n+            // distribution (See System.supportedBlockchains) - either way we have a better\n+            // current block height than any checkpoint.\n+            \n+            ewmSignalBlockChain (ewm, EMPTY_HASH_INIT, ewm->blockHeight, 0);\n \n             // ... and then just ignore nodes\n \n@@ -735,8 +494,9 @@ ewmCreate (BREthereumNetwork network,\n         }\n     }\n \n-    // mark as \'sync in progress\' - we can\'t sent transactions until we have the nonce.\n+    BRSetFreeAll (walletStates, (void (*) (void*)) walletStateRelease);\n \n+    // mark as \'sync in progress\' - we can\'t sent transactions until we have the nonce.\n     return ewm;\n \n }\n@@ -838,6 +598,9 @@ ewmStop (BREthereumEWM ewm) {\n \n     // Stop the EWM thread\n     eventHandlerStop(ewm->handler);\n+\n+    // Close the file service.\n+    fileServiceClose (ewm->fs);\n }\n \n /// MARK: - Connect / Disconnect\n@@ -1239,6 +1002,7 @@ ewmUpdateMode (BREthereumEWM ewm,\n         BRSetOf(BREthereumBlock) blocks;\n         BRSetOf(BREthereumTransaction) transactions;\n         BRSetOf(BREthereumLog) logs;\n+        BRSetOf(BREthereumWalletState) states;\n \n         // We have BCS in all modes but in BRD_ONLY mode it is never started.\n \n@@ -1292,7 +1056,8 @@ ewmUpdateMode (BREthereumEWM ewm,\n \n             case SYNC_MODE_P2P_WITH_BRD_SYNC:\n             case SYNC_MODE_P2P_ONLY:\n-                ewmCreateInitialSets (ewm, ewm->network, ewm->accountTimestamp, &transactions, &logs, &nodes, &blocks, &tokens);\n+                ewmCreateInitialSets (ewm, ewm->network, ewm->accountTimestamp,\n+                                      &transactions, &logs, &nodes, &blocks, &tokens, &states);\n \n                 ewm->bcs = bcsCreate (ewm->network,\n                                       primaryAddress,\n@@ -1302,6 +1067,8 @@ ewmUpdateMode (BREthereumEWM ewm,\n                                       blocks,\n                                       transactions,\n                                       logs);\n+\n+                BRSetFreeAll (states, (void (*) (void*)) walletStateRelease);\n                 break;\n          }\n \n@@ -2042,10 +1809,7 @@ ewmHandleAccountState (BREthereumEWM ewm,\n \n     eth_log("EWM", "AccountState: Nonce: %" PRIu64, accountState.nonce);\n \n-    accountSetAddressNonce(ewm->account, accountGetPrimaryAddress(ewm->account),\n-                           accountState.nonce,\n-                           ETHEREUM_BOOLEAN_FALSE);\n-\n+    ewmHandleAnnounceNonce (ewm, accountGetPrimaryAddress(ewm->account), accountState.nonce, 0);\n     ewmSignalBalance(ewm, amountCreateEther(accountState.balance));\n     pthread_mutex_unlock(&ewm->lock);\n }\n@@ -2365,10 +2129,10 @@ ewmHandleSaveBlocks (BREthereumEWM ewm,\n     size_t count = array_count(blocks);\n \n     eth_log("EWM", "Save Blocks (Storage): %zu", count);\n-    fileServiceClear(ewm->fs, fileServiceTypeBlocks);\n+    fileServiceClear(ewm->fs, ewmFileServiceTypeBlocks);\n \n     for (size_t index = 0; index < count; index++)\n-        fileServiceSave (ewm->fs, fileServiceTypeBlocks, blocks[index]);\n+        fileServiceSave (ewm->fs, ewmFileServiceTypeBlocks, blocks[index]);\n     array_free (blocks);\n }\n \n@@ -2378,10 +2142,10 @@ ewmHandleSaveNodes (BREthereumEWM ewm,\n     size_t count = array_count(nodes);\n \n     eth_log("EWM", "Save Nodes (Storage): %zu", count);\n-    fileServiceClear(ewm->fs, fileServiceTypeNodes);\n+    fileServiceClear(ewm->fs, ewmFileServiceTypeNodes);\n \n     for (size_t index = 0; index < count; index++)\n-        fileServiceSave(ewm->fs, fileServiceTypeNodes, nodes[index]);\n+        fileServiceSave(ewm->fs, ewmFileServiceTypeNodes, nodes[index]);\n \n     array_free (nodes);\n }\n@@ -2399,11 +2163,11 @@ ewmHandleSaveTransaction (BREthereumEWM ewm,\n             fileName);\n \n     if (CLIENT_CHANGE_REM == type || CLIENT_CHANGE_UPD == type)\n-        fileServiceRemove (ewm->fs, fileServiceTypeTransactions,\n-                           fileServiceTypeTransactionV1Identifier (ewm, ewm->fs, transaction));\n+        fileServiceRemove (ewm->fs, ewmFileServiceTypeTransactions,\n+                           fileServiceGetIdentifier(ewm->fs, ewmFileServiceTypeTransactions, transaction));\n \n     if (CLIENT_CHANGE_ADD == type || CLIENT_CHANGE_UPD == type)\n-        fileServiceSave (ewm->fs, fileServiceTypeTransactions, transaction);\n+        fileServiceSave (ewm->fs, ewmFileServiceTypeTransactions, transaction);\n }\n \n extern void\n@@ -2419,11 +2183,47 @@ ewmHandleSaveLog (BREthereumEWM ewm,\n             filename);\n \n     if (CLIENT_CHANGE_REM == type || CLIENT_CHANGE_UPD == type)\n-        fileServiceRemove (ewm->fs, fileServiceTypeLogs,\n-                           fileServiceTypeLogV1Identifier(ewm, ewm->fs, log));\n+        fileServiceRemove (ewm->fs, ewmFileServiceTypeLogs,\n+                           fileServiceGetIdentifier (ewm->fs, ewmFileServiceTypeLogs, log));\n \n     if (CLIENT_CHANGE_ADD == type || CLIENT_CHANGE_UPD == type)\n-        fileServiceSave (ewm->fs, fileServiceTypeLogs, log);\n+        fileServiceSave (ewm->fs, ewmFileServiceTypeLogs, log);\n+}\n+\n+extern void\n+ewmHandleSaveWallet (BREthereumEWM ewm,\n+                     BREthereumWallet wallet,\n+                     BREthereumClientChangeType type) {\n+    BREthereumWalletState state = walletStateCreate (wallet);\n+\n+    // If this is the primaryWallet, hack in the nonce\n+    if (wallet == ewm->walletHoldingEther) {\n+        walletStateSetNonce (state,\n+                             accountGetAddressNonce (ewm->account,\n+                                                     accountGetPrimaryAddress(ewm->account)));\n+    }\n+\n+    BREthereumHash hash = walletStateGetHash(state);\n+    BREthereumHashString filename;\n+    hashFillString(hash, filename);\n+\n+    eth_log ("EWM", "Wallet: Save: %s: %s",\n+             CLIENT_CHANGE_TYPE_NAME (type),\n+             filename);\n+\n+    switch (type) {\n+        case CLIENT_CHANGE_REM:\n+            fileServiceRemove (ewm->fs, ewmFileServiceTypeWallets,\n+                               fileServiceGetIdentifier (ewm->fs, ewmFileServiceTypeWallets, state));\n+            break;\n+\n+        case CLIENT_CHANGE_ADD:\n+        case CLIENT_CHANGE_UPD:\n+            fileServiceSave (ewm->fs, ewmFileServiceTypeWallets, state);\n+            break;\n+    }\n+\n+    walletStateRelease (state);\n }\n \n extern void\n@@ -3101,11 +2901,11 @@ ewmCreateToken (BREthereumEWM ewm,\n     // `ewmHandleAnnounceToken()`) and one in `cryptoWalletManagerInstall...()` (on some App\n     // listener thread).  Such a description, used here, is troubling in and of itself.\n \n-    BREthereumAddress addr  = addressCreate(address);\n-    BREthereumToken   token = ewmLookupToken (ewm, addr);\n+    BREthereumAddress addr = addressCreate(address);\n \n-    // Lock over BRSetAdd() and tokenUpdate()\n+    // Lock over BRSetGet(), BRSetAdd() and tokenUpdate()\n     pthread_mutex_lock (&ewm->lock);\n+    BREthereumToken token = (BREthereumToken) BRSetGet (ewm->tokens, &addr);\n     if (NULL == token) {\n         token = tokenCreate (address,\n                              symbol,\n@@ -3127,6 +2927,6 @@ ewmCreateToken (BREthereumEWM ewm,\n     }\n     pthread_mutex_unlock (&ewm->lock);\n \n-    fileServiceSave (ewm->fs, fileServiceTypeTokens, token);\n+    fileServiceSave (ewm->fs, ewmFileServiceTypeTokens, token);\n     return token;\n }\ndiff --git a/ethereum/ewm/BREthereumEWMClient.c b/ethereum/ewm/BREthereumEWMClient.c\nindex 6be07efb..7aa02a8b 100644\n--- a/ethereum/ewm/BREthereumEWMClient.c\n+++ b/ethereum/ewm/BREthereumEWMClient.c\n@@ -350,12 +350,17 @@ ewmAnnounceNonce (BREthereumEWM ewm,\n  */\n extern void\n ewmHandleAnnounceNonce (BREthereumEWM ewm,\n-                              BREthereumAddress address,\n-                              uint64_t nonce,\n-                              int rid) {\n-    //    BREthereumEncodedAddress address = accountGetPrimaryAddress (ewmGetAccount(ewm));\n-    //    assert (ETHEREUM_BOOLEAN_IS_TRUE (addressHasString(address, strAddress)));\n-    accountSetAddressNonce(ewm->account, accountGetPrimaryAddress(ewm->account), nonce, ETHEREUM_BOOLEAN_FALSE);\n+                        BREthereumAddress address,\n+                        uint64_t newNonce,\n+                        int rid) {\n+    uint64_t oldNonce = accountGetAddressNonce (ewm->account, address);\n+    if (oldNonce != newNonce) {\n+        // This may not change the nonce\n+        accountSetAddressNonce (ewm->account, address, newNonce, ETHEREUM_BOOLEAN_FALSE);\n+        // Only save the primaryWallet if the nonce has, in fact, changed.\n+        if (oldNonce != accountGetAddressNonce (ewm->account, address))\n+            ewmHandleSaveWallet (ewm, ewmGetWallet(ewm), CLIENT_CHANGE_UPD);\n+    }\n }\n \n // ==============================================================================================\n@@ -365,9 +370,9 @@ ewmHandleAnnounceNonce (BREthereumEWM ewm,\n \n \n extern void\n-ewmHandleAnnounceTransaction(BREthereumEWM ewm,\n-                                   BREthereumEWMClientAnnounceTransactionBundle *bundle,\n-                                   int id) {\n+ewmHandleAnnounceTransaction (BREthereumEWM ewm,\n+                              BREthereumEWMClientAnnounceTransactionBundle *bundle,\n+                              int id) {\n     switch (ewm->mode) {\n         case SYNC_MODE_BRD_ONLY:\n         case SYNC_MODE_BRD_WITH_P2P_SEND: {\n@@ -888,11 +893,20 @@ ewmAnnounceTokenComplete (BREthereumEWM ewm,\n //\n // Client\n //\n+static int\n+ewmNeedWalletSave (BREthereumEWM ewm,\n+                   BREthereumWallet wid,\n+                   BREthereumWalletEvent event) {\n+    return (WALLET_EVENT_BALANCE_UPDATED == event.type); // CREATED?\n+}\n \n extern void\n ewmHandleWalletEvent(BREthereumEWM ewm,\n                      BREthereumWallet wid,\n                      BREthereumWalletEvent event) {\n+    if (ewmNeedWalletSave (ewm, wid, event))\n+        ewmHandleSaveWallet (ewm, wid, CLIENT_CHANGE_UPD);\n+\n     ewm->client.funcWalletEvent (ewm->client.context,\n                                  ewm,\n                                  wid,\ndiff --git a/ethereum/ewm/BREthereumEWMPersist.c b/ethereum/ewm/BREthereumEWMPersist.c\nnew file mode 100644\nindex 00000000..104afa5b\n--- /dev/null\n+++ b/ethereum/ewm/BREthereumEWMPersist.c\n@@ -0,0 +1,421 @@\n+//\n+//  BREthereumEWMPersist.c\n+//  BRCore\n+//\n+//  Created by Ed Gamble on 9/24/19.\n+//  Copyright © 2019 Breadwinner AG. All rights reserved.\n+//\n+//\n+//  See the LICENSE file at the project root for license information.\n+//  See the CONTRIBUTORS file at the project root for a list of contributors.\n+\n+#include "BREthereumEWMPrivate.h"\n+\n+/// MARK: - Transaction File Service\n+#define fileServiceTypeTransactions "transactions"\n+\n+enum {\n+    EWM_TRANSACTION_VERSION_1\n+};\n+\n+static UInt256\n+fileServiceTypeTransactionV1Identifier (BRFileServiceContext context,\n+                                        BRFileService fs,\n+                                        const void *entity) {\n+    BREthereumTransaction transaction = (BREthereumTransaction) entity;\n+    BREthereumHash hash = transactionGetHash(transaction);\n+\n+    UInt256 result;\n+    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n+    return result;\n+}\n+\n+static uint8_t *\n+fileServiceTypeTransactionV1Writer (BRFileServiceContext context,\n+                                    BRFileService fs,\n+                                    const void* entity,\n+                                    uint32_t *bytesCount) {\n+    BREthereumEWM ewm = context;\n+    BREthereumTransaction transaction = (BREthereumTransaction) entity;\n+\n+    BRRlpItem item = transactionRlpEncode(transaction, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n+    BRRlpData data = rlpGetData (ewm->coder, item);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    *bytesCount = (uint32_t) data.bytesCount;\n+    return data.bytes;\n+}\n+\n+static void *\n+fileServiceTypeTransactionV1Reader (BRFileServiceContext context,\n+                                    BRFileService fs,\n+                                    uint8_t *bytes,\n+                                    uint32_t bytesCount) {\n+    BREthereumEWM ewm = context;\n+\n+    BRRlpData data = { bytesCount, bytes };\n+    BRRlpItem item = rlpGetItem (ewm->coder, data);\n+\n+    BREthereumTransaction transaction = transactionRlpDecode(item, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    return transaction;\n+}\n+\n+/// MARK: - Log File Service\n+\n+#define fileServiceTypeLogs "logs"\n+\n+enum {\n+    EWM_LOG_VERSION_1\n+};\n+\n+static UInt256\n+fileServiceTypeLogV1Identifier (BRFileServiceContext context,\n+                                BRFileService fs,\n+                                const void *entity) {\n+    const BREthereumLog log = (BREthereumLog) entity;\n+    BREthereumHash hash = logGetHash( log);\n+\n+    UInt256 result;\n+    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n+    return result;\n+}\n+\n+static uint8_t *\n+fileServiceTypeLogV1Writer (BRFileServiceContext context,\n+                            BRFileService fs,\n+                            const void* entity,\n+                            uint32_t *bytesCount) {\n+    BREthereumEWM ewm = context;\n+    BREthereumLog log = (BREthereumLog) entity;\n+\n+    BRRlpItem item = logRlpEncode (log, RLP_TYPE_ARCHIVE, ewm->coder);\n+    BRRlpData data = rlpGetData (ewm->coder, item);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    *bytesCount = (uint32_t) data.bytesCount;\n+    return data.bytes;\n+}\n+\n+static void *\n+fileServiceTypeLogV1Reader (BRFileServiceContext context,\n+                            BRFileService fs,\n+                            uint8_t *bytes,\n+                            uint32_t bytesCount) {\n+    BREthereumEWM ewm = context;\n+\n+    BRRlpData data = { bytesCount, bytes };\n+    BRRlpItem item = rlpGetItem (ewm->coder, data);\n+\n+    BREthereumLog log = logRlpDecode(item, RLP_TYPE_ARCHIVE, ewm->coder);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    return log;\n+}\n+\n+/// MARK: - Block File Service\n+\n+#define fileServiceTypeBlocks "blocks"\n+enum {\n+    EWM_BLOCK_VERSION_1\n+};\n+\n+static UInt256\n+fileServiceTypeBlockV1Identifier (BRFileServiceContext context,\n+                                  BRFileService fs,\n+                                  const void *entity) {\n+    const BREthereumBlock block = (BREthereumBlock) entity;\n+    BREthereumHash hash = blockGetHash(block);\n+\n+    UInt256 result;\n+    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n+    return result;\n+}\n+\n+static uint8_t *\n+fileServiceTypeBlockV1Writer (BRFileServiceContext context,\n+                              BRFileService fs,\n+                              const void* entity,\n+                              uint32_t *bytesCount) {\n+    BREthereumEWM ewm = context;\n+    BREthereumBlock block = (BREthereumBlock) entity;\n+\n+    BRRlpItem item = blockRlpEncode(block, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n+    BRRlpData data = rlpGetData (ewm->coder, item);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    *bytesCount = (uint32_t) data.bytesCount;\n+    return data.bytes;\n+}\n+\n+static void *\n+fileServiceTypeBlockV1Reader (BRFileServiceContext context,\n+                              BRFileService fs,\n+                              uint8_t *bytes,\n+                              uint32_t bytesCount) {\n+    BREthereumEWM ewm = context;\n+\n+    BRRlpData data = { bytesCount, bytes };\n+    BRRlpItem item = rlpGetItem (ewm->coder, data);\n+\n+    BREthereumBlock block = blockRlpDecode (item, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    return block;\n+}\n+\n+// MARK: - Node File Service\n+\n+#define fileServiceTypeNodes "nodes"\n+enum {\n+    EWM_NODE_VERSION_1\n+};\n+\n+static UInt256\n+fileServiceTypeNodeV1Identifier (BRFileServiceContext context,\n+                                 BRFileService fs,\n+                                 const void *entity) {\n+    const BREthereumNodeConfig node = (BREthereumNodeConfig) entity;\n+\n+    BREthereumHash hash = nodeConfigGetHash(node);\n+\n+    UInt256 result;\n+    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n+    return result;\n+}\n+\n+static uint8_t *\n+fileServiceTypeNodeV1Writer (BRFileServiceContext context,\n+                             BRFileService fs,\n+                             const void* entity,\n+                             uint32_t *bytesCount) {\n+    BREthereumEWM ewm = context;\n+    const BREthereumNodeConfig node = (BREthereumNodeConfig) entity;\n+\n+    BRRlpItem item = nodeConfigEncode (node, ewm->coder);\n+    BRRlpData data = rlpGetData (ewm->coder, item);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    *bytesCount = (uint32_t) data.bytesCount;\n+    return data.bytes;\n+}\n+\n+static void *\n+fileServiceTypeNodeV1Reader (BRFileServiceContext context,\n+                             BRFileService fs,\n+                             uint8_t *bytes,\n+                             uint32_t bytesCount) {\n+    BREthereumEWM ewm = context;\n+\n+    BRRlpData data = { bytesCount, bytes };\n+    BRRlpItem item = rlpGetItem (ewm->coder, data);\n+\n+    BREthereumNodeConfig node = nodeConfigDecode (item, ewm->coder);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    return node;\n+}\n+\n+/// MARK: - Token File Service\n+\n+#define fileServiceTypeTokens "tokens"\n+\n+enum {\n+    EWM_TOKEN_VERSION_1\n+};\n+\n+static UInt256\n+fileServiceTypeTokenV1Identifier (BRFileServiceContext context,\n+                                        BRFileService fs,\n+                                        const void *entity) {\n+    BREthereumToken token = (BREthereumToken) entity;\n+    BREthereumHash hash = tokenGetHash(token);\n+\n+    UInt256 result;\n+    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n+    return result;\n+}\n+\n+static uint8_t *\n+fileServiceTypeTokenV1Writer (BRFileServiceContext context,\n+                                    BRFileService fs,\n+                                    const void* entity,\n+                                    uint32_t *bytesCount) {\n+    BREthereumEWM ewm = context;\n+    BREthereumToken token = (BREthereumToken) entity;\n+\n+    BRRlpItem item = tokenEncode(token, ewm->coder);\n+    BRRlpData data = rlpGetData (ewm->coder, item);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    *bytesCount = (uint32_t) data.bytesCount;\n+    return data.bytes;\n+}\n+\n+static void *\n+fileServiceTypeTokenV1Reader (BRFileServiceContext context,\n+                                    BRFileService fs,\n+                                    uint8_t *bytes,\n+                                    uint32_t bytesCount) {\n+    BREthereumEWM ewm = context;\n+\n+    BRRlpData data = { bytesCount, bytes };\n+    BRRlpItem item = rlpGetItem (ewm->coder, data);\n+\n+    BREthereumToken token = tokenDecode(item, ewm->coder);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    return token;\n+}\n+\n+/// MARK: - Wallet File Service\n+\n+#define fileServiceTypeWallets "wallets"\n+\n+enum {\n+    EWM_WALLET_VERSION_1\n+};\n+\n+static UInt256\n+fileServiceTypeWalletV1Identifier (BRFileServiceContext context,\n+                                   BRFileService fs,\n+                                   const void *entity) {\n+    BREthereumWalletState state = (BREthereumWalletState) entity;\n+    BREthereumHash hash = walletStateGetHash (state);\n+\n+    UInt256 result;\n+    memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES);\n+    return result;\n+}\n+\n+static uint8_t *\n+fileServiceTypeWalletV1Writer (BRFileServiceContext context,\n+                               BRFileService fs,\n+                               const void* entity,\n+                               uint32_t *bytesCount) {\n+    BREthereumEWM ewm = context;\n+    BREthereumWalletState state = (BREthereumWalletState) entity;\n+\n+    BRRlpItem item = walletStateEncode (state, ewm->coder);\n+    BRRlpData data = rlpGetData (ewm->coder, item);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    *bytesCount = (uint32_t) data.bytesCount;\n+    return data.bytes;\n+}\n+\n+static void *\n+fileServiceTypeWalletV1Reader (BRFileServiceContext context,\n+                               BRFileService fs,\n+                               uint8_t *bytes,\n+                               uint32_t bytesCount) {\n+    BREthereumEWM ewm = context;\n+\n+    BRRlpData data = { bytesCount, bytes };\n+    BRRlpItem item = rlpGetItem (ewm->coder, data);\n+\n+    BREthereumWalletState state = walletStateDecode(item, ewm->coder);\n+    rlpReleaseItem (ewm->coder, item);\n+\n+    return state;\n+}\n+\n+\n+#define fileServiceSpecificationsCount      (6)\n+static BRFileServiceTypeSpecification fileServiceSpecifications[] = {\n+    {\n+        fileServiceTypeTransactions,\n+        EWM_TRANSACTION_VERSION_1,\n+        1,\n+        {\n+            {\n+                EWM_TRANSACTION_VERSION_1,\n+                fileServiceTypeTransactionV1Identifier,\n+                fileServiceTypeTransactionV1Reader,\n+                fileServiceTypeTransactionV1Writer\n+            }\n+        }\n+    },\n+\n+    {\n+        fileServiceTypeLogs,\n+        EWM_LOG_VERSION_1,\n+        1,\n+        {\n+            {\n+                EWM_LOG_VERSION_1,\n+                fileServiceTypeLogV1Identifier,\n+                fileServiceTypeLogV1Reader,\n+                fileServiceTypeLogV1Writer\n+            }\n+        }\n+    },\n+\n+    {\n+        fileServiceTypeBlocks,\n+        EWM_BLOCK_VERSION_1,\n+        1,\n+        {\n+            {\n+                EWM_BLOCK_VERSION_1,\n+                fileServiceTypeBlockV1Identifier,\n+                fileServiceTypeBlockV1Reader,\n+                fileServiceTypeBlockV1Writer\n+            }\n+        }\n+    },\n+\n+    {\n+        fileServiceTypeNodes,\n+        EWM_NODE_VERSION_1,\n+        1,\n+        {\n+            {\n+                EWM_NODE_VERSION_1,\n+                fileServiceTypeNodeV1Identifier,\n+                fileServiceTypeNodeV1Reader,\n+                fileServiceTypeNodeV1Writer\n+            }\n+        }\n+    },\n+\n+    {\n+        fileServiceTypeTokens,\n+        EWM_TOKEN_VERSION_1,\n+        1,\n+        {\n+            {\n+                EWM_TOKEN_VERSION_1,\n+                fileServiceTypeTokenV1Identifier,\n+                fileServiceTypeTokenV1Reader,\n+                fileServiceTypeTokenV1Writer\n+            }\n+        }\n+    },\n+\n+    {\n+        fileServiceTypeWallets,\n+        EWM_WALLET_VERSION_1,\n+        1,\n+        {\n+            {\n+                EWM_TOKEN_VERSION_1,\n+                fileServiceTypeWalletV1Identifier,\n+                fileServiceTypeWalletV1Reader,\n+                fileServiceTypeWalletV1Writer\n+            }\n+        }\n+    }\n+};\n+\n+const char *ewmFileServiceTypeTransactions = fileServiceTypeTransactions;\n+const char *ewmFileServiceTypeLogs         = fileServiceTypeLogs;\n+const char *ewmFileServiceTypeBlocks       = fileServiceTypeBlocks;\n+const char *ewmFileServiceTypeNodes        = fileServiceTypeNodes;\n+const char *ewmFileServiceTypeTokens       = fileServiceTypeTokens;\n+const char *ewmFileServiceTypeWallets      = fileServiceTypeWallets;\n+\n+size_t ewmFileServiceSpecificationsCount = fileServiceSpecificationsCount;\n+BRFileServiceTypeSpecification *ewmFileServiceSpecifications = fileServiceSpecifications;\n+\ndiff --git a/ethereum/ewm/BREthereumEWMPrivate.h b/ethereum/ewm/BREthereumEWMPrivate.h\nindex 07a49819..a878b02a 100644\n--- a/ethereum/ewm/BREthereumEWMPrivate.h\n+++ b/ethereum/ewm/BREthereumEWMPrivate.h\n@@ -11,6 +11,7 @@\n #ifndef BR_Ethereum_EWM_Private_H\n #define BR_Ethereum_EWM_Private_H\n \n+#include <stdint.h>\n #include <pthread.h>\n #include "support/BRFileService.h"\n #include "ethereum/blockchain/BREthereumBlockChain.h"\n@@ -303,6 +304,11 @@ ewmHandleSaveLog (BREthereumEWM ewm,\n                   BREthereumLog log,\n                   BREthereumClientChangeType type);\n \n+extern void\n+ewmHandleSaveWallet (BREthereumEWM ewm,\n+                     BREthereumWallet wallet,\n+                     BREthereumClientChangeType type);\n+\n //\n // Signal/Handle Sync (BCS Callback)\n //\n@@ -611,6 +617,18 @@ ewmHandleEWMEvent(BREthereumEWM ewm,\n extern const BREventType *ewmEventTypes[];\n extern const unsigned int ewmEventTypesCount;\n \n+/// MARK: - File Service\n+\n+extern const char *ewmFileServiceTypeTransactions;\n+extern const char *ewmFileServiceTypeLogs;\n+extern const char *ewmFileServiceTypeBlocks;\n+extern const char *ewmFileServiceTypeNodes;\n+extern const char *ewmFileServiceTypeTokens;\n+extern const char *ewmFileServiceTypeWallets;\n+\n+extern size_t ewmFileServiceSpecificationsCount;\n+extern BRFileServiceTypeSpecification *ewmFileServiceSpecifications;\n+\n #ifdef __cplusplus\n }\n #endif\ndiff --git a/ethereum/ewm/BREthereumWallet.c b/ethereum/ewm/BREthereumWallet.c\nindex 88c0c424..4b474c55 100644\n--- a/ethereum/ewm/BREthereumWallet.c\n+++ b/ethereum/ewm/BREthereumWallet.c\n@@ -604,6 +604,128 @@ walletTransferErrored (BREthereumWallet wallet,\n                          transactionStatusCreateErrored(reason));\n }\n #endif // 0\n+\n+/// MARK: - Wallet State\n+\n+struct BREthereumWalletStateRecord {\n+    // This is a token address or, if Ether, FAKE_ETHER_ADDRESS_INIT\n+    BREthereumAddress address;\n+\n+    // Normally we would save the wallet\'s balance as `BREthereumAmount` but that concept includes\n+    // the `BREthereumToken` if the amount is \'token based\'.  In the context of WalletState we\n+    // do not have tokens available (tokens are held as `BRSet` in `BREthereumEWM`, not globally).\n+    // So, we\'ll save the balance as `UInt256` and then when recovering the wallet use this\n+    // `amount` with the above `address` to create a proper balance.\n+    UInt256 amount;\n+\n+    // Include the account nonce if the this wallet state is for ETHER.  Hackily.\n+    uint64_t nonce;\n+};\n+\n+#define FAKE_ETHER_ADDRESS_INIT   ((const BREthereumAddress) { \\\n+0xff, 0xff, 0xff, 0xff,   \\\n+0xff, 0xff, 0xff, 0xff,   \\\n+0xff, 0xff, 0xff, 0xff,   \\\n+0xff, 0xff, 0xff, 0xff,   \\\n+0xff, 0xff, 0xff, 0xff \\\n+})\n+\n+extern BREthereumWalletState\n+walletStateCreate (const BREthereumWallet wallet) {\n+    BREthereumWalletState state = malloc (sizeof (struct BREthereumWalletStateRecord));\n+\n+    state->nonce = 0;\n+    \n+    BREthereumAmount balance = walletGetBalance(wallet);\n+    BREthereumToken  token   = walletGetToken (wallet);\n+\n+    if (NULL == token) {\n+        state->address = FAKE_ETHER_ADDRESS_INIT;\n+        state->amount  = etherGetValue (amountGetEther(balance), WEI);\n+    }\n+    else {\n+        state->address = tokenGetAddressRaw(token);\n+        state->amount  = amountGetTokenQuantity(balance).valueAsInteger;\n+    }\n+\n+    return state;\n+}\n+\n+extern void\n+walletStateRelease (BREthereumWalletState state) {\n+    free (state);\n+}\n+\n+extern BREthereumAddress\n+walletStateGetAddress (const BREthereumWalletState walletState) {\n+    return (ETHEREUM_BOOLEAN_IS_TRUE (addressEqual (FAKE_ETHER_ADDRESS_INIT, walletState->address))\n+            ? EMPTY_ADDRESS_INIT\n+            : walletState->address);\n+}\n+\n+extern UInt256\n+walletStateGetAmount (const BREthereumWalletState walletState) {\n+    return walletState->amount;\n+}\n+\n+extern uint64_t\n+walletStateGetNonce (const BREthereumWalletState walletState) {\n+    return walletState->nonce;\n+}\n+\n+extern void\n+walletStateSetNonce (BREthereumWalletState walletState,\n+                     uint64_t nonce) {\n+    walletState->nonce = nonce;\n+}\n+\n+extern BRRlpItem\n+walletStateEncode (const BREthereumWalletState state,\n+                   BRRlpCoder coder) {\n+    return rlpEncodeList (coder, 3,\n+                          addressRlpEncode (state->address, coder),\n+                          rlpEncodeUInt256 (coder, state->amount, 0),\n+                          rlpEncodeUInt64  (coder, state->nonce, 0));\n+}\n+\n+extern BREthereumWalletState\n+walletStateDecode (BRRlpItem item,\n+                   BRRlpCoder coder) {\n+    BREthereumWalletState state = malloc (sizeof (struct BREthereumWalletStateRecord));\n+\n+    size_t itemsCount = 0;\n+    const BRRlpItem *items = rlpDecodeList (coder, item, &itemsCount);\n+    assert (3 == itemsCount);\n+\n+    state->address = addressRlpDecode (items[0], coder);\n+    state->amount  = rlpDecodeUInt256 (coder, items[1], 0);\n+    state->nonce   = rlpDecodeUInt64  (coder, items[2], 0);\n+\n+    return state;\n+}\n+\n+extern BREthereumHash\n+walletStateGetHash (const BREthereumWalletState state) {\n+    return addressGetHash (state->address);\n+}\n+\n+static inline size_t\n+walletStateHashValue (const void *t)\n+{\n+    return addressHashValue(((BREthereumWalletState) t)->address);\n+}\n+\n+static inline int\n+walletStateHashEqual (const void *t1, const void *t2) {\n+    return t1 == t2 || addressHashEqual (((BREthereumWalletState) t1)->address,\n+                                         ((BREthereumWalletState) t2)->address);\n+}\n+\n+extern BRSetOf(BREthereumWalletState)\n+walletStateSetCreate (size_t capacity) {\n+    return BRSetNew (walletStateHashValue, walletStateHashEqual, capacity);\n+}\n+\n /*\n  * https://medium.com/blockchain-musings/how-to-create-raw-transfers-in-ethereum-part-1-1df91abdba7c\n  *\ndiff --git a/ethereum/ewm/BREthereumWallet.h b/ethereum/ewm/BREthereumWallet.h\nindex 543b005d..93a4ed0a 100644\n--- a/ethereum/ewm/BREthereumWallet.h\n+++ b/ethereum/ewm/BREthereumWallet.h\n@@ -272,6 +272,46 @@ private_extern int\n walletHasTransfer (BREthereumWallet wallet,\n                    BREthereumTransfer transaction);\n \n+/// MARK: - Persisted Wallet State;\n+\n+typedef struct BREthereumWalletStateRecord *BREthereumWalletState;\n+\n+extern BREthereumWalletState\n+walletStateCreate (const BREthereumWallet wallet);\n+\n+extern void\n+walletStateRelease (BREthereumWalletState state);\n+\n+/**\n+ * If WalletState holds Ether, then the address will be EMPTY_ADDRESS_INIT\n+ */\n+extern BREthereumAddress\n+walletStateGetAddress (const BREthereumWalletState walletState);\n+\n+extern UInt256\n+walletStateGetAmount (const BREthereumWalletState walletState);\n+\n+extern uint64_t\n+walletStateGetNonce (const BREthereumWalletState walletState);\n+\n+extern void\n+walletStateSetNonce (BREthereumWalletState walletState,\n+                     uint64_t nonce);\n+\n+extern BRRlpItem\n+walletStateEncode (const BREthereumWalletState walletState,\n+                   BRRlpCoder coder);\n+\n+extern BREthereumWalletState\n+walletStateDecode (BRRlpItem item,\n+                   BRRlpCoder coder);\n+\n+extern BREthereumHash\n+walletStateGetHash (const BREthereumWalletState walletState);\n+\n+extern BRSetOf(BREthereumWalletState)\n+walletStateSetCreate (size_t capacity);\n+\n #ifdef __cplusplus\n }\n #endif\ndiff --git a/ethereum/rlp/BRRlpCoder.c b/ethereum/rlp/BRRlpCoder.c\nindex aa6030cb..1ccb16d1 100644\n--- a/ethereum/rlp/BRRlpCoder.c\n+++ b/ethereum/rlp/BRRlpCoder.c\n@@ -16,9 +16,6 @@\n #include "ethereum/util/BRUtil.h"\n #include "BRRlpCoder.h"\n \n-static void\n-rlpCoderReclaimInternal (BRRlpCoder coder) ;\n-\n static int\n rlpDecodeStringEmptyCheck (BRRlpCoder coder, BRRlpItem item);\n \n@@ -125,21 +122,8 @@ rlpCoderCreate (void) {\n     return coder;\n }\n \n-extern void\n-rlpCoderRelease (BRRlpCoder coder) {\n-    pthread_mutex_lock(&coder->lock);\n-\n-    // Every single Item must be returned!\n-    assert (NULL == coder->busy);\n-    rlpCoderReclaimInternal (coder);\n-\n-    pthread_mutex_unlock(&coder->lock);\n-    pthread_mutex_destroy(&coder->lock);\n-    free (coder);\n-}\n-\n static void\n-rlpCoderReclaimInternal (BRRlpCoder coder) {\n+_rlpCoderReclaimInternal (BRRlpCoder coder) {\n     BRRlpItem item = coder->free;\n     while (item != NULL) {\n         BRRlpItem next = item->next;   // save \'next\' before release...\n@@ -153,22 +137,26 @@ rlpCoderReclaimInternal (BRRlpCoder coder) {\n extern void\n rlpCoderReclaim (BRRlpCoder coder) {\n     pthread_mutex_lock(&coder->lock);\n-    rlpCoderReclaimInternal (coder);\n+    _rlpCoderReclaimInternal (coder);\n     pthread_mutex_unlock(&coder->lock);\n }\n \n-extern int\n-rlpCoderBusyCount (BRRlpCoder coder) {\n-    int count = 0;\n-    for (BRRlpItem found = coder->busy; NULL != found; found = found->next)\n-        count++;\n-    return count;\n+extern void\n+rlpCoderRelease (BRRlpCoder coder) {\n+    pthread_mutex_lock(&coder->lock);\n+\n+    // Every single Item must be returned!\n+    assert (NULL == coder->busy);\n+    _rlpCoderReclaimInternal (coder);\n+\n+    pthread_mutex_unlock(&coder->lock);\n+    pthread_mutex_destroy(&coder->lock);\n+    free (coder);\n }\n \n static BRRlpItem\n-rlpCoderAcquireItem (BRRlpCoder coder) {\n+_rlpCoderAcquireItemInternal (BRRlpCoder coder) {\n     BRRlpItem item = NULL;\n-    pthread_mutex_lock(&coder->lock);\n \n     // Get `item` from `coder->free` or `calloc`\n     if (NULL != coder->free) {\n@@ -188,13 +176,19 @@ rlpCoderAcquireItem (BRRlpCoder coder) {\n     // Update `coder` to show `item` as busy.\n     coder->busy = item;\n \n+    return item;\n+}\n+\n+static BRRlpItem\n+rlpCoderAcquireItem (BRRlpCoder coder) {\n+    pthread_mutex_lock(&coder->lock);\n+    BRRlpItem item = _rlpCoderAcquireItemInternal (coder);\n     pthread_mutex_unlock(&coder->lock);\n     return item;\n }\n \n static void\n-rlpCoderReturnItem (BRRlpCoder coder, BRRlpItem prev, BRRlpItem item, BRRlpItem next) {\n-    pthread_mutex_lock(&coder->lock);\n+_rlpCoderReturnItemInternal (BRRlpCoder coder, BRRlpItem prev, BRRlpItem item, BRRlpItem next) {\n     assert (NULL == item->next       && NULL == item->prev &&\n             0    == item->bytesCount && 0    == item->itemsCount);\n \n@@ -215,17 +209,26 @@ rlpCoderReturnItem (BRRlpCoder coder, BRRlpItem prev, BRRlpItem item, BRRlpItem\n \n     // Update `coder` to show `item` as free.\n     coder->free = item;\n-\n-    pthread_mutex_unlock(&coder->lock);\n }\n \n static void\n-itemRelease (BRRlpCoder coder, BRRlpItem item) {\n+_rlpCoderReleaseItemInternal (BRRlpCoder coder, BRRlpItem item) {\n+    for (size_t index = 0; index < item->itemsCount; index++)\n+        _rlpCoderReleaseItemInternal (coder, item->items[index]);\n+\n+    // Surely get these before itemReleaseMemory() blows them away.\n     BRRlpItem prev = item->prev;\n     BRRlpItem next = item->next;\n \n     itemReleaseMemory(item);\n-    rlpCoderReturnItem(coder, prev, item, next);\n+    _rlpCoderReturnItemInternal (coder, prev, item, next);\n+}\n+\n+static void\n+rlpCoderReleaseItem (BRRlpCoder coder, BRRlpItem item) {\n+    pthread_mutex_lock(&coder->lock);\n+    _rlpCoderReleaseItemInternal (coder, item);\n+    pthread_mutex_unlock(&coder->lock);\n }\n \n static int\n@@ -277,84 +280,6 @@ rlpCoderHasFailed (BRRlpCoder coder) {\n     return coder->failed;\n }\n \n-#if 0\n-static BRRlpItem\n-itemCreate (BRRlpCoder coder,\n-            uint8_t *bytes, size_t bytesCount, int takeBytes) {\n-    BRRlpItem item = calloc (1, sizeof (struct BRRlpItemRecord));\n-\n-    item->type = CODER_ITEM;\n-    item->bytesCount = bytesCount;\n-    if (takeBytes)\n-        item->bytes = bytes;\n-    else {\n-        item->bytes = (item->bytesCount > ITEM_DEFAULT_BYTES_COUNT\n-                       ? malloc (item->bytesCount)\n-                       : item->bytesArray);\n-        memcpy (item->bytes, bytes, item->bytesCount);\n-    }\n-    item->itemsCount = 0;\n-    item->items = NULL;\n-\n-    return item;\n-}\n-\n-static BRRlpItem\n-itemCreateList (BRRlpCoder coder,\n-                uint8_t *bytes, size_t bytesCount, int takeBytes,\n-                BRRlpItem *items, size_t itemsCount) {\n-    BRRlpItem item = itemCreate(coder, bytes, bytesCount, takeBytes);\n-    item->type = CODER_LIST;\n-    item->itemsCount = itemsCount;\n-    item->items = (item->itemsCount > ITEM_DEFAULT_ITEMS_COUNT\n-                   ? calloc (item->itemsCount, sizeof (BRRlpItem))\n-                   : item->itemsArray);\n-    for (int i = 0; i < itemsCount; i++)\n-        item->items[i] = items[i];\n-    \n-    return item;\n-}\n-#endif\n-\n-/**\n- * Return a new BRRlpContext by appending the two provided contexts.  Both provided contexts\n- * must be for CODER_ITEM (othewise an \'assert\' is raised); the appending is performed by simply\n- * concatenating the two context\'s byte arrays.\n- *\n- * If release is TRUE, then both the provided contexts are released; thereby freeing their memory.\n- *\n- */\n-#if 0\n-static BRRlpItem\n-itemCreateAppend (BRRlpCoder coder, BRRlpItem context1, BRRlpItem context2, int release) {\n-    assert (CODER_ITEM == context1->type && CODER_ITEM == context2->type);\n-\n-    BRRlpItem item = calloc (1, sizeof (struct BRRlpItemRecord));\n-\n-    item->type = CODER_ITEM;\n-    \n-    item->bytesCount = context1->bytesCount + context2->bytesCount;\n-    item->bytes = (item->bytesCount > ITEM_DEFAULT_BYTES_COUNT\n-                   ? malloc (item->bytesCount)\n-                   : item->bytesArray);\n-\n-    memcpy (&item->bytes[0], context1->bytes, context1->bytesCount);\n-    memcpy (&item->bytes[context1->bytesCount], context2->bytes, context2->bytesCount);\n-\n-    item->itemsCount = 0;\n-    item->items = NULL;\n-    \n-    if (release) {\n-        //        assert (context2.bytes != context1.bytes || context2.bytes == NULL || context1.bytes == NULL);\n-        //        assert (context2.items != context1.items || context2.items == NULL || context1.items == NULL);\n-        itemRelease(coder, context1);\n-        itemRelease(coder, context2);\n-    }\n-    \n-    return item;\n-}\n-#endif\n-\n // The largest number supported for encoding is a UInt256 - which is representable as 32 bytes.\n #define CODER_NUMBER_BYTES_LIMIT    (256/8)\n \n@@ -1071,9 +996,7 @@ rlpShowItemInternal (BRRlpCoder coder, BRRlpItem context, const char *topic, int\n extern void\n rlpReleaseItem (BRRlpCoder coder, BRRlpItem item) {\n     assert (itemIsValid(coder, item));\n-    for (size_t index = 0; index < item->itemsCount; index++)\n-        rlpReleaseItem(coder, item->items[index]);\n-    itemRelease(coder, item);\n+    rlpCoderReleaseItem (coder, item);\n }\n \n extern void\ndiff --git a/generic/BRGenericWalletManager.c b/generic/BRGenericWalletManager.c\nindex c0a75964..542e5fbf 100644\n--- a/generic/BRGenericWalletManager.c\n+++ b/generic/BRGenericWalletManager.c\n@@ -152,9 +152,16 @@ gwmCreate (BRGenericClient client,\n \n extern void\n gwmRelease (BRGenericWalletManager gwm) {\n+    gwmDisconnect (gwm);\n     free (gwm);\n }\n \n+extern void\n+gwmStop (BRGenericWalletManager gwm) {\n+    eventHandlerStop (gwm->handler);\n+    fileServiceClose (gwm->fileService);\n+}\n+\n extern BRGenericHandlers\n gwmGetHandlers (BRGenericWalletManager gwm) {\n     return gwm->handlers;\n@@ -173,7 +180,7 @@ gwmConnect (BRGenericWalletManager gwm) {\n \n extern void\n gwmDisconnect (BRGenericWalletManager gwm) {\n-    eventHandlerStop (gwm->handler);\n+    gwmStop (gwm);  // This is questionable.\n     // Event\n \n }\ndiff --git a/generic/BRGenericWalletManager.h b/generic/BRGenericWalletManager.h\nindex f92d03d1..c40d796e 100644\n--- a/generic/BRGenericWalletManager.h\n+++ b/generic/BRGenericWalletManager.h\n@@ -95,6 +95,9 @@ extern "C" {\n     extern void\n     gwmRelease (BRGenericWalletManager gwm);\n \n+    extern void\n+    gwmStop (BRGenericWalletManager gwm);\n+\n     extern void\n     gwmConnect (BRGenericWalletManager gwm);\n \ndiff --git a/support/BRFileService.c b/support/BRFileService.c\nindex 988e44c1..a9dea423 100644\n--- a/support/BRFileService.c\n+++ b/support/BRFileService.c\n@@ -186,6 +186,7 @@ struct BRFileServiceRecord {\n     sqlite3_stmt *sdbDeleteStmt;\n     sqlite3_stmt *sdbDeleteAllTypeStmt;\n     sqlite3_stmt *sdbDeleteAllStmt;\n+    uint8_t  sdbClosed;\n \n     char *currency;\n     char *network;\n@@ -250,6 +251,7 @@ fileServiceCreate (const char *basePath,\n \n     fs->sdb = NULL;\n     fs->sdbPath = NULL;\n+    fs->sdbClosed = 0;\n \n     // Save currency and network\n     fs->currency = strdup (currency);\n@@ -347,11 +349,46 @@ fileServiceCreate (const char *basePath,\n     return fs;\n }\n \n+static void\n+_fileServiceFinalizeStmt (BRFileService fs, sqlite3_stmt **stmt) {\n+    if (NULL != stmt && NULL != *stmt) {\n+        sqlite3_finalize (*stmt);\n+        *stmt = NULL;\n+    }\n+}\n+\n+static void\n+_fileServiceCloseInternal (BRFileService fs) {\n+    if (fs->sdbClosed) return;\n+\n+    fs->sdbClosed = 1;\n+    _fileServiceFinalizeStmt (fs, &fs->sdbInsertStmt);\n+    _fileServiceFinalizeStmt (fs, &fs->sdbSelectStmt);\n+    _fileServiceFinalizeStmt (fs, &fs->sdbSelectAllStmt);\n+    _fileServiceFinalizeStmt (fs, &fs->sdbUpdateStmt);\n+    _fileServiceFinalizeStmt (fs, &fs->sdbDeleteStmt);\n+    _fileServiceFinalizeStmt (fs, &fs->sdbDeleteAllTypeStmt);\n+    _fileServiceFinalizeStmt (fs, &fs->sdbDeleteAllStmt);\n+\n+    if (NULL != fs->sdb) sqlite3_close (fs->sdb);\n+    fs->sdb = NULL;\n+}\n+\n+extern void\n+fileServiceClose (BRFileService fs) {\n+    pthread_mutex_lock (&fs->lock);\n+    _fileServiceCloseInternal(fs);\n+    pthread_mutex_unlock (&fs->lock);\n+}\n+\n // This is callable with a partially allocated BRFileService.  So, be\n // careful with fields that might not yet exist.\n extern void\n fileServiceRelease (BRFileService fs) {\n     pthread_mutex_lock (&fs->lock);\n+\n+    _fileServiceCloseInternal(fs);\n+\n     if (NULL != fs->entityTypes) {\n         size_t typesCount = array_count(fs->entityTypes);\n         for (size_t index = 0; index < typesCount; index++)\n@@ -363,15 +400,6 @@ fileServiceRelease (BRFileService fs) {\n     if (NULL != fs->currency) free (fs->currency);\n     if (NULL != fs->sdbPath)  free (fs->sdbPath);\n \n-    if (NULL != fs->sdbInsertStmt)        sqlite3_finalize (fs->sdbInsertStmt);\n-    if (NULL != fs->sdbSelectStmt)        sqlite3_finalize (fs->sdbSelectStmt);\n-    if (NULL != fs->sdbSelectAllStmt)     sqlite3_finalize (fs->sdbSelectAllStmt);\n-    if (NULL != fs->sdbUpdateStmt)        sqlite3_finalize (fs->sdbUpdateStmt);\n-    if (NULL != fs->sdbDeleteStmt)        sqlite3_finalize (fs->sdbDeleteStmt);\n-    if (NULL != fs->sdbDeleteAllTypeStmt) sqlite3_finalize (fs->sdbDeleteAllTypeStmt);\n-    if (NULL != fs->sdbDeleteAllStmt)     sqlite3_finalize (fs->sdbDeleteAllStmt);\n-\n-    if (NULL != fs->sdb) sqlite3_close (fs->sdb);\n     pthread_mutex_unlock (&fs->lock);\n     pthread_mutex_destroy(&fs->lock);\n \n@@ -477,7 +505,7 @@ fileServiceFailedSDB (BRFileService fs,\n     return fileServiceFailedInternal (fs, releaseLock, NULL, NULL,\n                                       (BRFileServiceError) {\n                                           FILE_SERVICE_SDB,\n-                                          { .sdb = { code }}\n+                                          { .sdb = { code, sqlite3_errstr(code) }}\n                                       });\n }\n \n@@ -549,6 +577,9 @@ _fileServiceSave (BRFileService fs,\n     if (needLock)\n         pthread_mutex_lock (&fs->lock);\n \n+    if (fs->sdbClosed)\n+        return fileServiceFailedImpl (fs, needLock, NULL, NULL, "closed");\n+\n     sqlite3_reset (fs->sdbInsertStmt);\n     sqlite3_clear_bindings(fs->sdbInsertStmt);\n \n@@ -570,8 +601,8 @@ _fileServiceSave (BRFileService fs,\n         return fileServiceFailedSDB (fs, needLock, status);\n     }\n \n-    // Execute the Update\n-    if (SQLITE_DONE != sqlite3_step (fs->sdbInsertStmt)) {\n+    status = sqlite3_step (fs->sdbInsertStmt);\n+    if (SQLITE_DONE != status) {\n         free (data);\n         return fileServiceFailedSDB (fs, needLock, status);\n     }\n@@ -606,6 +637,9 @@ fileServiceLoad (BRFileService fs,\n     sqlite3_status_code status;\n \n     pthread_mutex_lock (&fs->lock);\n+    if (fs->sdbClosed)\n+        return fileServiceFailedImpl (fs, 1, NULL, NULL, "closed");\n+\n     sqlite3_reset (fs->sdbSelectAllStmt);\n \n     status = sqlite3_bind_text (fs->sdbSelectAllStmt, 1, type, -1, SQLITE_STATIC);\n@@ -719,6 +753,9 @@ fileServiceRemove (BRFileService fs,\n     sqlite3_status_code status;\n \n     pthread_mutex_lock (&fs->lock);\n+    if (fs->sdbClosed)\n+        return fileServiceFailedImpl (fs, 1, NULL, NULL, "closed");\n+\n     sqlite3_reset (fs->sdbDeleteStmt);\n \n     status = sqlite3_bind_text (fs->sdbDeleteStmt, 1, type, -1, SQLITE_STATIC);\n@@ -729,7 +766,8 @@ fileServiceRemove (BRFileService fs,\n     if (SQLITE_OK != status)\n         return fileServiceFailedSDB (fs, 1, status);\n \n-    if (SQLITE_DONE != sqlite3_step (fs->sdbDeleteStmt))\n+    status = sqlite3_step (fs->sdbDeleteStmt);\n+    if (SQLITE_DONE != status)\n         return fileServiceFailedSDB (fs, 1, status);\n \n     pthread_mutex_unlock (&fs->lock);\n@@ -745,13 +783,17 @@ fileServiceClearForType (BRFileService fs,\n     sqlite3_status_code status;\n \n     pthread_mutex_lock (&fs->lock);\n+    if (fs->sdbClosed)\n+        return fileServiceFailedImpl (fs, 1, NULL, NULL, "closed");\n+\n     sqlite3_reset (fs->sdbDeleteAllTypeStmt);\n \n     status = sqlite3_bind_text (fs->sdbDeleteAllTypeStmt, 1, type, -1, SQLITE_STATIC);\n     if (SQLITE_OK != status)\n         return fileServiceFailedSDB (fs, 1, status);\n \n-    if (SQLITE_DONE != sqlite3_step (fs->sdbDeleteAllTypeStmt))\n+    status = sqlite3_step (fs->sdbDeleteAllTypeStmt);\n+    if (SQLITE_DONE != status)\n         return fileServiceFailedSDB (fs, 1, status);\n \n     pthread_mutex_unlock (&fs->lock);\n@@ -778,6 +820,20 @@ fileServiceClearAll (BRFileService fs) {\n     return success;\n }\n \n+extern UInt256\n+fileServiceGetIdentifier (BRFileService fs,\n+                          const char *type,\n+                          const void *entity) {\n+\n+    BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type);\n+    if (NULL == entityType) { fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type"); return UINT256_ZERO; };\n+\n+    BRFileServiceEntityHandler *handler = fileServiceEntityTypeLookupHandler(entityType, entityType->currentVersion);\n+    if (NULL == handler) { fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type handler"); return UINT256_ZERO; };\n+\n+    return handler->identifier (handler->context, fs, entity);\n+}\n+\n extern int\n fileServiceDefineType (BRFileService fs,\n                        const char *type,\ndiff --git a/support/BRFileService.h b/support/BRFileService.h\nindex 83e688eb..33003838 100644\n--- a/support/BRFileService.h\n+++ b/support/BRFileService.h\n@@ -65,6 +65,7 @@ typedef struct {\n \n         struct {\n             int code;  // sqlite3_status_code\n+            const char *reason;\n         } sdb;\n \n         struct {\n@@ -90,9 +91,20 @@ fileServiceCreate (const char *basePath,\n                    BRFileServiceContext context,\n                    BRFileServiceErrorHandler handler);\n \n+/**\n+ * Release fs.  This will close `fs` if it hasn\'t been already and then free the memory and any\n+ * other resources associaed with the fs (such as locks).\n+ */\n extern void\n fileServiceRelease (BRFileService fs);\n \n+/**\n+ * Close fs.  This will close the DB associated with `fs`.  The `fs` must not be used after closing;\n+ * if it is then an IMPL error is raised.  This can be called multiple times (but shouldn\'t be).\n+ */\n+extern void\n+fileServiceClose (BRFileService fs);\n+\n extern void\n fileServiceSetErrorHandler (BRFileService fs,\n                             BRFileServiceContext context,\n@@ -133,6 +145,11 @@ fileServiceClear (BRFileService fs,\n extern int\n fileServiceClearAll (BRFileService fs);\n \n+extern UInt256\n+fileServiceGetIdentifier (BRFileService fs,\n+                          const char *type,\n+                          const void *entity);\n+\n /**\n  * A function type to produce an identifer from an entity.  The identifer must be constant for\n  * a particulary entity through time.  The identifier is used to derive a filename (more generally\n', 'commit_subject': "Merge branch 'crypto' into ripple", 'repo_name': 'breadwallet/breadwallet-core', 'committer': {'name': 'Ed Gamble', 'email': 'a162caffcb6897220f4d1cf28164d73cb91993a8@breadwallet.com', 'time_sec': '1569948400', 'tz_offset': '-420', 'date': {'seconds': '1569948400'}}, 'commit_tree': 'ed628c42366105e89f0ad739fff1850a95ff6a49', 'commit_message': "Merge branch 'crypto' into ripple\n", 'language_name': 'Shell', 'license': 'mit', 'hunks': "@@ -65,6 +65,7 @@ typedef struct {\n \n         struct {\n             int code;  // sqlite3_status_code\n+            const char *reason;\n         } sdb;\n \n         struct {\n@@ -90,9 +91,20 @@ fileServiceCreate (const char *basePath,\n                    BRFileServiceContext context,\n                    BRFileServiceErrorHandler handler);\n \n+/**\n+ * Release fs.  This will close `fs` if it hasn't been already and then free the memory and any\n+ * other resources associaed with the fs (such as locks).\n+ */\n extern void\n fileServiceRelease (BRFileService fs);\n \n+/**\n+ * Close fs.  This will close the DB associated with `fs`.  The `fs` must not be used after closing;\n+ * if it is then an IMPL error is raised.  This can be called multiple times (but shouldn't be).\n+ */\n+extern void\n+fileServiceClose (BRFileService fs);\n+\n extern void\n fileServiceSetErrorHandler (BRFileService fs,\n                             BRFileServiceContext context,\n@@ -133,6 +145,11 @@ fileServiceClear (BRFileService fs,\n extern int\n fileServiceClearAll (BRFileService fs);\n \n+extern UInt256\n+fileServiceGetIdentifier (BRFileService fs,\n+                          const char *type,\n+                          const void *entity);\n+\n /**\n  * A function type to produce an identifer from an entity.  The identifer must be constant for\n  * a particulary entity through time.  The identifier is used to derive a filename (more generally\n", 'addition_count': 17, 'deletion_count': 0, 'src_file': 'a/support/BRFileService.h', 'tgt_file': 'b/support/BRFileService.h', 'file_extension': '.h', 'before_file': '//\n//  BRFileService.h\n//  Core\n//\n//  Created by Richard Evers on 1/4/19.\n//  Copyright © 2019 breadwallet LLC\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of this software and associated documentation files (the "Software"), to deal\n//  in the Software without restriction, including without limitation the rights\n//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n//  copies of the Software, and to permit persons to whom the Software is\n//  furnished to do so, subject to the following conditions:\n//\n//  The above copyright notice and this permission notice shall be included in\n//  all copies or substantial portions of the Software.\n//\n//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n//  THE SOFTWARE.\n\n#ifndef BRFileService_h\n#define BRFileService_h\n\n#include <stdlib.h>\n#include "BRSet.h"\n#include "BRInt.h"\n\n// Both Bitcoin and Ethereum Wallet Managers include the ability to save and load peers, block,\n// transactions and logs (for Ethereum) to the file system.  But, they both implement the file\n// operations independently.  Pull out the implementation into BRFileService.\n//\n// Allow each WalletManager (Bitcoin, Bitcash, Ethereum) to create their own BRFileService (storing\n// stuff in a subdirectory specific to the manager+network+whatever for a given base path).  Allow\n// each WalletManager to further define \'persistent types\' (peers, blocks, etc) saved.  Allow for\n// a versioning system (at least on read; everything gets written/saved w/ the latest version).\n// Allow an upgrade path from existing IOS/Andriod Sqlite3 databases.\n//\ntypedef struct BRFileServiceRecord *BRFileService;\n\n/// A context used in callbacks\ntypedef void* BRFileServiceContext;\n\ntypedef enum {\n    FILE_SERVICE_IMPL,              // generally a fatal condition\n    FILE_SERVICE_UNIX,              // something in the file system (fopen, fwrite, ... errorred)\n    FILE_SERVICE_SDB,               // something in the sqlite3 database\n    FILE_SERVICE_ENTITY             // entity read/write (parse/serialize) error\n} BRFileServiceErrorType;\n\ntypedef struct {\n    BRFileServiceErrorType type;\n    union {\n        struct {\n            const char *reason;\n        } impl;\n\n        struct {\n            int error;\n        } unix;\n\n        struct {\n            int code;  // sqlite3_status_code\n        } sdb;\n\n        struct {\n            const char *type;\n            const char *reason;\n        } entity;\n    } u;\n} BRFileServiceError;\n\ntypedef void\n(*BRFileServiceErrorHandler) (BRFileServiceContext context,\n                              BRFileService fs,\n                              BRFileServiceError error);\n\n/// This *must* be the same fixed size type forever.  It is uint8_t.\ntypedef uint8_t BRFileServiceVersion;\n\n/// TODO: There are limitations on `currency`, `network`, and `type`.\nextern BRFileService\nfileServiceCreate (const char *basePath,\n                   const char *currency,\n                   const char *network,\n                   BRFileServiceContext context,\n                   BRFileServiceErrorHandler handler);\n\nextern void\nfileServiceRelease (BRFileService fs);\n\nextern void\nfileServiceSetErrorHandler (BRFileService fs,\n                            BRFileServiceContext context,\n                            BRFileServiceErrorHandler handler);\n\n/**\n * Load all entities of `type` adding each to `results`.  If there is an error then the\n * fileServices\' error handler is invoked and 0 is returned\n *\n * @param fs The fileServie\n * @param results A BRSet within which to store the results.  The type stored in the BRSet must\n *     be consistent with type recovered from the file system.\n * @param type The type to restore\n * @param updateVersion If true (1) update old versions with newer ones.\n *\n * @return true (1) if success, false (0) otherwise;\n */\nextern int\nfileServiceLoad (BRFileService fs,\n                 BRSet *results,\n                 const char *type,   /* blocks, peers, transactions, logs, ... */\n                 int updateVersion);\n\nextern int  // 1 -> success, 0 -> failure\nfileServiceSave (BRFileService fs,\n                 const char *type,  /* block, peers, transactions, logs, ... */\n                 const void *entity);     /* BRMerkleBlock*, BRTransaction, BREthereumTransaction, ... */\n\nextern int\nfileServiceRemove (BRFileService fs,\n                   const char *type,\n                   UInt256 identifier);\n\nextern int\nfileServiceReplace (BRFileService fs,\n                    const char *type,\n                    const void **entities,\nextern int\nfileServiceClearAll (BRFileService fs);\n\n/**\n * A function type to produce an identifer from an entity.  The identifer must be constant for\n * a particulary entity through time.  The identifier is used to derive a filename (more generally\n                          const char *type,\n                          const void *entity);\n\n/**\n * A function type to produce an identifer from an entity.  The identifer must be constant for\n * a particulary entity through time.  The identifier is used to derive a filename (more generally\n * a path and filename).\n */\ntypedef UInt256\n(*BRFileServiceIdentifier) (BRFileServiceContext context,\n                            BRFileService fs,\n                            const void* entity);\n\n/**\n * A function type to read an entity from a byte array.  You own the entity.\n */\ntypedef void*\n(*BRFileServiceReader) (BRFileServiceContext context,\n                        BRFileService fs,\n                        uint8_t *bytes,\n                        uint32_t bytesCount);\n\n/**\n * A function type to write an entity to a byte array.  You own the byte array.\n */\ntypedef uint8_t*\n(*BRFileServiceWriter) (BRFileServiceContext context,\n                        BRFileService fs,\n                        const void* entity,\n                        uint32_t *bytesCount);\n\n/// TODO: There is a limitation on `type`.\n\n/**\n * Define a \'type\', such as {block, peer, transaction, logs, etc}, that is to be stored in the\n * file system.\n *\n * @param fs the file service\n * @param type the type\n * @param context an arbitrary value to be passed to the type-specific functions.\n * @param version the entity version handled by the type-specific functions.\n * @param identifier the function that produces the identifier\n * @param reader the function the produces an entity from a byte array\n * @param writer the function that produces a byte array from an entity.\n *\n * @return true (1) if success, false (0) otherwise\n */\nextern int\nfileServiceDefineType (BRFileService fs,\n                       const char *type,\n                       BRFileServiceVersion version,\n                       BRFileServiceContext context,\n                       BRFileServiceIdentifier identifier,\n                       BRFileServiceReader reader,\n                       BRFileServiceWriter writer);\n\nextern int\nfileServiceDefineCurrentVersion (BRFileService fs,\n                                 const char *type,\n                                 BRFileServiceVersion version);\n\n// Version limit can increase with maximum number of version, historically.\n#define FILE_SERVICE_TYPE_SPECIFICATION_NUMBER_OF_VERSION_LIMIT   (5)\n\ntypedef struct {\n    const char *type;\n    BRFileServiceVersion defaultVersion;\n    size_t versionsCount;\n    struct {\n        BRFileServiceVersion version;\n        BRFileServiceIdentifier identifier;\n        BRFileServiceReader reader;\n        BRFileServiceWriter writer;\n    } versions [FILE_SERVICE_TYPE_SPECIFICATION_NUMBER_OF_VERSION_LIMIT];\n} BRFileServiceTypeSpecification;\n\nextern BRFileService\nfileServiceCreateFromTypeSpecfications (const char *basePath,\n                                        const char *currency,\n                                        const char *network,\n                                        BRFileServiceContext context,\n                                        BRFileServiceErrorHandler handler,\n                                        size_t specificationsCount,\n                                        BRFileServiceTypeSpecification *specfications);\n\n///\n/// Deletes file system data\n///\n/// @param basePath\n/// @param currency\n/// @param network\n///\n/// @return 0 on success, errno on failure\n///\nextern int\nfileServiceWipe (const char *basePath,\n                 const char *currency,\n                 const char *network);\n\n#endif /* BRFileService_h */\n', 'message': "Merge branch 'crypto' into ripple\n", 'author_name': 'Ed Gamble', 'lang': 'h'}