<NME> BRRlpCoder.h
<BEF> //
//  rlp
//  Core Ethereum
//
//  Created by Ed Gamble on 2/25/18.
//  Copyright © 2018-2019 Breadwinner AG.  All rights reserved.
//
//  See the LICENSE file at the project root for license information.
//  See the CONTRIBUTORS file at the project root for a list of contributors.

#ifndef BR_RLP_Coder_H
#define BR_RLP_Coder_H

#include <stddef.h>
#include <stdint.h>
#include "support/BRInt.h"

#ifdef __cplusplus
extern "C" {
#endif

//
// RLP Coder
//
typedef struct BRRlpCoderRecord *BRRlpCoder;

extern BRRlpCoder
rlpCoderCreate (void);

extern void
rlpCoderRelease (BRRlpCoder coder);

/**
 * Reclaim coder memory. A coder can hold memory to avoid repeated free/malloc calls.  If
 * desired one can reclaim coder memory that is unused.
 */
extern void
rlpCoderReclaim (BRRlpCoder coder);

extern void
rlpCoderSetFailed (BRRlpCoder coder);

extern void
rlpCoderClrFailed (BRRlpCoder coder);

extern int
rlpCoderHasFailed (BRRlpCoder coder);

//
// RLP Data
//
typedef struct {
    size_t bytesCount;
    uint8_t *bytes;
} BRRlpData;

extern BRRlpData
rlpDataCopy (BRRlpData data);

extern void
rlpDataRelease (BRRlpData data);

} BRRlpData;

extern BRRlpData
createRlpDataEmpty (void);

extern void
rlpDataRelease (BRRlpData data);

/**
 * Convet the bytes in `data` into an `item`.  If `data` represents a RLP list, then `item` will
 * represent a list (thus `data` is 'walked' to identify subitems, including sublists).
 */
extern BRRlpItem
rlpDataGetItem (BRRlpCoder coder, BRRlpData data);

/**
 * Return the RLP data associated with `item`.  You own this data and must call
 * rlpDataRelese().
 */
extern BRRlpData
rlpItemGetData (BRRlpCoder coder, BRRlpItem item);

/**
 * Return the RLP data associated with `item`.  You DO NOT own this data; you must not
 * modify the data nor release the data nor hold the data.  [The returned data is a direct
 * pointer into the `coder` memory and will become invalid on coder release.
 */
extern BRRlpData
rlpItemGetDataSharedDontRelease (BRRlpCoder coder, BRRlpItem item);

/**
 * Extract the `bytes` and `bytesCount` for `item`.  The returns `bytes` will be the complete
 * RLP encoding for `item` which includes the RLP encoding of length.  Contrast this with
 * rlpDecodeItemBytes() which only returns the RLP encoding data w/o the length.
 * Extract the `item` data as a copy, filling `bytes` and `bytesCount`.
 *
 * TODO: ?? Hold onto BRRlpItem 'forever'... then try to use... will fail because 'coder'
 * TODO: will not have 'context' ??
 */
//extern void
//rlpDataExtract (BRRlpItem item, uint8_t **bytes, size_t *bytesCount);

//
// UInt64
//
extern BRRlpItem
rlpEncodeUInt64(BRRlpCoder coder, uint64_t value, int zeroAsEmptyString);

extern uint64_t
rlpDecodeUInt64(BRRlpCoder coder, BRRlpItem item, int zeroAsEmptyString);

//
// UInt256
//
extern BRRlpItem
rlpEncodeUInt256(BRRlpCoder coder, UInt256 value, int zeroAsEmptyString);

extern UInt256
rlpDecodeUInt256(BRRlpCoder coder, BRRlpItem item, int zeroAsEmptyString);

//
// Double
//
extern BRRlpItem
rlpEncodeDouble(BRRlpCoder coder, double value);

extern double
rlpDecodeDouble(BRRlpCoder coder, BRRlpItem item);

//
// Bytes
//
extern BRRlpItem
rlpEncodeBytes (BRRlpCoder coder, uint8_t *bytes, size_t bytesCount);

extern BRRlpItem
rlpEncodeBytesPurgeLeadingZeros (BRRlpCoder coder, uint8_t *bytes, size_t bytesCount);

/**
 * Extract the `data` for `item` as the item's bytes w/o the RLP encoding of length.  Thus, if
 * you used rlpEncodeItemBytes() to encode `bytes` and `bytesCount`, then the result of
 * rlpDecodeItemBytes() will be `data` with exactly the same data as was encoded.
 */
extern BRRlpData
rlpDecodeBytes (BRRlpCoder coder, BRRlpItem item);

extern BRRlpData
rlpDecodeBytesSharedDontRelease (BRRlpCoder coder, BRRlpItem item);

extern BRRlpData
rlpDecodeListSharedDontRelease (BRRlpCoder coder, BRRlpItem item);

//
// String
//
extern BRRlpItem
rlpEncodeString (BRRlpCoder coder, const char *string);

extern char *
rlpDecodeString (BRRlpCoder coder, BRRlpItem item);

extern int
rlpDecodeStringCheck (BRRlpCoder coder, BRRlpItem item);

//
// Hex String
//
extern BRRlpItem
rlpEncodeHexString (BRRlpCoder coder, const char *string);

extern char *
rlpDecodeHexString (BRRlpCoder coder, BRRlpItem item, const char *prefix);

//
// List
//
extern BRRlpItem
rlpEncodeList1 (BRRlpCoder coder, BRRlpItem item1);

extern BRRlpItem
rlpEncodeList2 (BRRlpCoder coder, BRRlpItem item1, BRRlpItem item2);

extern BRRlpItem
rlpEncodeList (BRRlpCoder coder, size_t count, ...);

extern BRRlpItem
rlpEncodeListItems (BRRlpCoder coder, BRRlpItem *items, size_t itemsCount);

extern const BRRlpItem *
rlpDecodeList (BRRlpCoder coder, BRRlpItem item, size_t *itemsCount);
    
//
// Show
//
extern void
rlpItemShow (BRRlpCoder coder, BRRlpItem item, const char *topic);

extern void
rlpDataShow (BRRlpData data, const char *topic);

//
// Decode RLPData directly to numbers
//   (Used for logGetData -> UInt256
//
extern UInt256
rlpDataDecodeUInt256 (BRRlpData data);

extern uint64_t
rlpDataDecodeUInt64 (BRRlpData data);

#ifdef __cplusplus
}
#endif

#endif //BR_RLP_Coder_H

<MSG> Merge branch 'feature/CORE-149' into ethereum

<DFF> @@ -63,7 +63,7 @@ typedef struct {
 } BRRlpData;
 
 extern BRRlpData
-createRlpDataEmpty (void);
+rlpDataCopy (BRRlpData data);
 
 extern void
 rlpDataRelease (BRRlpData data);

{'commit': 'diff --git a/Swift/BRCore.xcodeproj/project.pbxproj b/Swift/BRCore.xcodeproj/project.pbxproj\nindex b4c45df2..3912acc7 100644\n--- a/Swift/BRCore.xcodeproj/project.pbxproj\n+++ b/Swift/BRCore.xcodeproj/project.pbxproj\n@@ -173,6 +173,8 @@\n \t\t3CD3049D213F281500A0CCED /* BREthereumProvision.c in Sources */ = {isa = PBXBuildFile; fileRef = 3CD3049C213F281500A0CCED /* BREthereumProvision.c */; };\n \t\t3CDA7648210015A5005F4CF8 /* BREthereum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CAB611C20B347A200810CE4 /* BREthereum.swift */; };\n \t\t3CDA764B210138B3005F4CF8 /* BREthereumEWMClientEvent.c in Sources */ = {isa = PBXBuildFile; fileRef = 3CDA764A210138B2005F4CF8 /* BREthereumEWMClientEvent.c */; };\n+\t\t3CF0FC9021657D7B000DE3FE /* BREthereumData.c in Sources */ = {isa = PBXBuildFile; fileRef = 3CF0FC8F21657D7A000DE3FE /* BREthereumData.c */; };\n+\t\t3CF0FC9121657D7B000DE3FE /* BREthereumData.c in Sources */ = {isa = PBXBuildFile; fileRef = 3CF0FC8F21657D7A000DE3FE /* BREthereumData.c */; };\n \t\tCA92F7902100F9CA0015C966 /* BRKeccak.c in Sources */ = {isa = PBXBuildFile; fileRef = CA92F78F2100F9CA0015C966 /* BRKeccak.c */; };\n /* End PBXBuildFile section */\n \n@@ -453,6 +455,8 @@\n \t\t3CD3049B213D8C4200A0CCED /* BREthereumProvision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BREthereumProvision.h; sourceTree = "<group>"; };\n \t\t3CD3049C213F281500A0CCED /* BREthereumProvision.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BREthereumProvision.c; sourceTree = "<group>"; };\n \t\t3CDA764A210138B2005F4CF8 /* BREthereumEWMClientEvent.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BREthereumEWMClientEvent.c; sourceTree = "<group>"; };\n+\t\t3CF0FC8E21657D7A000DE3FE /* BREthereumData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BREthereumData.h; sourceTree = "<group>"; };\n+\t\t3CF0FC8F21657D7A000DE3FE /* BREthereumData.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BREthereumData.c; sourceTree = "<group>"; };\n \t\tCA92F78E2100F9CA0015C966 /* BRKeccak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BRKeccak.h; sourceTree = "<group>"; };\n \t\tCA92F78F2100F9CA0015C966 /* BRKeccak.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BRKeccak.c; sourceTree = "<group>"; };\n /* End PBXFileReference section */\n@@ -653,6 +657,8 @@\n \t\t\t\t3C2A53B520AF595400C430F6 /* BREthereumGas.c */,\n \t\t\t\t3C2A53B420AF595400C430F6 /* BREthereumHash.h */,\n \t\t\t\t3C2A53B020AF595400C430F6 /* BREthereumHash.c */,\n+\t\t\t\t3CF0FC8E21657D7A000DE3FE /* BREthereumData.h */,\n+\t\t\t\t3CF0FC8F21657D7A000DE3FE /* BREthereumData.c */,\n \t\t\t\t3C2A53B220AF595400C430F6 /* BREthereumAddress.h */,\n \t\t\t\t3C2A53B920AF595400C430F6 /* BREthereumAddress.c */,\n \t\t\t\t3C2A53B720AF595400C430F6 /* BREthereumSignature.h */,\n@@ -1153,6 +1159,7 @@\n \t\t\t\t3C6B173B2131CE12003C313B /* BREthereumHash.c in Sources */,\n \t\t\t\t3C6B173C2131CE12003C313B /* BREthereumAddress.c in Sources */,\n \t\t\t\t3C6B173D2131CE12003C313B /* BREthereumSignature.c in Sources */,\n+\t\t\t\t3CF0FC9121657D7B000DE3FE /* BREthereumData.c in Sources */,\n \t\t\t\t3C6B173E2131CE12003C313B /* BREthereumTransaction.c in Sources */,\n \t\t\t\t3C6B173F2131CE12003C313B /* BREthereumBloomFilter.c in Sources */,\n \t\t\t\t3C6B17402131CE12003C313B /* BREthereumNetwork.c in Sources */,\n@@ -1263,6 +1270,7 @@\n \t\t\t\t3CAB60CD20AF8D1A00810CE4 /* BREthereumEWM.c in Sources */,\n \t\t\t\t3CAB60D120AF8D1A00810CE4 /* BRRlpCoder.c in Sources */,\n \t\t\t\t3CAB60D220AF8D1A00810CE4 /* BRUtilMath.c in Sources */,\n+\t\t\t\t3CF0FC9021657D7B000DE3FE /* BREthereumData.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\t3CAB60D520AF8D1A00810CE4 /* BRBCashAddr.c in Sources */,\ndiff --git a/Swift/BRCore/BREthereum.swift b/Swift/BRCore/BREthereum.swift\nindex 878939f3..0a3d2254 100644\n--- a/Swift/BRCore/BREthereum.swift\n+++ b/Swift/BRCore/BREthereum.swift\n@@ -19,7 +19,9 @@ public typealias EthereumListenerId = EthereumReferenceId\n // Access to BRCore/BREthereum types\n public typealias BRCoreEWM = BREthereumEWM // OpaquePointer\n \n-// MARK: Reference\n+///\n+/// MARK: Reference\n+///\n \n ///\n /// Core Ethereum *does not* allow direct access to Core \'C\' Memory; instead we use references\n@@ -57,7 +59,9 @@ public protocol EthereumReferenceWithDefaultUnit : EthereumReference {\n     var unit : EthereumAmountUnit { get }\n }\n \n-// MARK: - Pointer\n+///\n+/// MARK: - Pointer\n+///\n \n ///\n /// An `EthereumPointer` holds an `OpaquePointer` to Ethereum Core memory.  This is used for\n@@ -75,7 +79,9 @@ extension EthereumPointer {\n     }\n }\n \n-// MARK: - Account\n+///\n+/// MARK: - Account\n+///\n \n ///\n /// An `EthereumAccount` is an `EthereumReference` for the User\'s account - with address,\n@@ -111,7 +117,9 @@ public struct EthereumAccount : EthereumReference {\n     }\n }\n \n-// MARK: - Network\n+///\n+/// MARK: - Network\n+///\n \n ///\n /// An `EthereumNetwork` represents one of a handful of Ethereum (Blockchain) Networks such as:\n@@ -139,7 +147,9 @@ extension EthereumNetwork : Equatable {\n     }\n }\n \n-// MARK: - Token\n+///\n+/// MARK: - Token\n+///\n \n ///\n /// An `Ethereum`Token` is a defined instantiation of an ERC20 Contract.\n@@ -200,7 +210,124 @@ public struct EthereumToken : EthereumPointer {\n     }()\n }\n \n-// MARK: - Wallet\n+\n+// MAKR: - Block\n+\n+///\n+/// An `EthereumBlock` represents a  ...\n+//\n+public struct EthereumBlock : EthereumReference {\n+    public weak var ewm : EthereumWalletManager?\n+    public let identifier : EthereumWalletId\n+\n+    init (ewm: EthereumWalletManager, identifier: EthereumAccountId) {\n+        self.ewm = ewm\n+        self.identifier = identifier\n+    }\n+\n+    var number : UInt64 {\n+        return ethereumBlockGetNumber (self.ewm!.core, identifier)\n+    }\n+\n+    var timestamp : UInt64 {\n+        return ethereumBlockGetTimestamp (self.ewm!.core, identifier)\n+    }\n+\n+    var hash : String {\n+        return asUTF8String(ethereumBlockGetHash (self.ewm!.core, identifier))\n+    }\n+}\n+\n+///\n+/// MARK: - Transfer\n+///\n+\n+///\n+/// An `EthereumTransaction` represents a transfer of ETHER or a specific TOKEN between two\n+/// accounts.\n+///\n+public struct EthereumTransfer : EthereumReferenceWithDefaultUnit {\n+    public weak var ewm : EthereumWalletManager?\n+    public let identifier : EthereumWalletId\n+    public let unit : EthereumAmountUnit\n+\n+    internal init (ewm : EthereumWalletManager, identifier : EthereumTransferId) {\n+        self.init (ewm: ewm,\n+                   identifier: identifier,\n+                   unit: EthereumAmountUnit.defaultUnit(\n+                    nil == ethereumTransferGetToken (ewm.core, identifier)))\n+    }\n+\n+    internal init (ewm : EthereumWalletManager, identifier : EthereumTransferId, unit: EthereumAmountUnit) {\n+        self.ewm = ewm\n+        self.identifier = identifier\n+        self.unit = unit\n+    }\n+\n+    public var hash : String {\n+        return asUTF8String (ethereumTransferGetHash (self.ewm!.core, self.identifier), true)\n+    }\n+\n+    public var sourceAddress : String {\n+        return asUTF8String (ethereumTransferGetSendAddress (self.ewm!.core, self.identifier), true)\n+    }\n+\n+    public var targetAddress : String {\n+        return asUTF8String (ethereumTransferGetRecvAddress (self.ewm!.core, self.identifier), true)\n+    }\n+\n+    public var amount : EthereumAmount {\n+        let amount : BREthereumAmount = ethereumTransferGetAmount (self.ewm!.core, self.identifier)\n+        return (AMOUNT_ETHER == amount.type\n+            ? EthereumAmount.ether(amount.u.ether.valueInWEI, unit.coreForEther)\n+            : EthereumAmount.token (amount.u.tokenQuantity.valueAsInteger,\n+                                    EthereumToken.lookup (core: amount.u.tokenQuantity.token),\n+                                    unit.coreForToken))\n+    }\n+\n+    public var fee : EthereumAmount {\n+        var overflow : Int32 = 0\n+        let fee : BREthereumEther = ethereumTransferGetFee(self.ewm!.core, self.identifier, &overflow);\n+        return EthereumAmount.ether(fee.valueInWEI, unit.coreForEther)\n+    }\n+\n+    public var confirmations : UInt64? {\n+        let confirmations = ethereumTransferGetBlockConfirmations(self.ewm!.core, self.identifier)\n+        return confirmations > 0 ? confirmations : nil\n+    }\n+\n+    public var confirmationBlockNumber : UInt64? {\n+        let number = ethereumTransferGetBlockNumber(self.ewm!.core, self.identifier)\n+        return number > 0 ? number : nil\n+    }\n+\n+    //    var gasPrice : EthereumAmount {\n+    //        let price : BREthereumAmount = ethereumTransferGetGasPriceToo (self.ewm!.core, self.identifier)\n+    //        return EthereumAmount.ether (price.u.ether.valueInWEI, WEI)\n+    //    }\n+    //\n+    //    var gasLimit : UInt64 {\n+    //        return ethereumTransactionGetGasLimit(self.ewm!.core, self.identifier)\n+    //    }\n+    //\n+    //    var gasUsed : UInt64 {\n+    //        return ethereumTransactionGetGasUsed (self.ewm!.core, self.identifier)\n+    //    }\n+    //\n+    //    var nonce : UInt64 {\n+    //        return ethereumTransactionGetNonce (self.ewm!.core, self.identifier)\n+    //    }\n+    //\n+    //    var blockNumber : UInt64 {\n+    //        return ethereumTransactionGetBlockNumber (self.ewm!.core, self.identifier)\n+    //    }\n+\n+    // State\n+}\n+\n+///\n+/// MARK: - Wallet\n+///\n \n ///\n /// An `EthereumWallet` holds a balance with ETHER or a TOKEN and is associated with a Network\n@@ -288,7 +415,7 @@ public struct EthereumWallet : EthereumReferenceWithDefaultUnit, Hashable {\n     }\n \n     //\n-    // Transaction\n+    // Transfer\n     //\n     public func createTransfer (recvAddress: String, amount: String, unit: EthereumAmountUnit) -> EthereumTransfer {\n         var status : BRCoreParseStatus = CORE_PARSE_OK\n@@ -346,119 +473,9 @@ public struct EthereumWallet : EthereumReferenceWithDefaultUnit, Hashable {\n     }\n }\n \n-// MAKR: - Block\n-\n-///\n-/// An `EthereumBlock` represents a  ...\n-//\n-public struct EthereumBlock : EthereumReference {\n-    public weak var ewm : EthereumWalletManager?\n-    public let identifier : EthereumWalletId\n-\n-    init (ewm: EthereumWalletManager, identifier: EthereumAccountId) {\n-        self.ewm = ewm\n-        self.identifier = identifier\n-    }\n-\n-    var number : UInt64 {\n-        return ethereumBlockGetNumber (self.ewm!.core, identifier)\n-    }\n-\n-    var timestamp : UInt64 {\n-        return ethereumBlockGetTimestamp (self.ewm!.core, identifier)\n-    }\n-\n-    var hash : String {\n-        return asUTF8String(ethereumBlockGetHash (self.ewm!.core, identifier))\n-    }\n-}\n-\n-// MARK: - Transaction\n-\n ///\n-/// An `EthereumTransaction` represents a transfer of ETHER or a specific TOKEN between two\n-/// accounts.\n+/// MARK: - Client\n ///\n-public struct EthereumTransfer : EthereumReferenceWithDefaultUnit {\n-    public weak var ewm : EthereumWalletManager?\n-    public let identifier : EthereumWalletId\n-    public let unit : EthereumAmountUnit\n-\n-    internal init (ewm : EthereumWalletManager, identifier : EthereumTransferId) {\n-        self.init (ewm: ewm,\n-                   identifier: identifier,\n-                   unit: EthereumAmountUnit.defaultUnit(\n-                    nil == ethereumTransferGetToken (ewm.core, identifier)))\n-    }\n-\n-    internal init (ewm : EthereumWalletManager, identifier : EthereumTransferId, unit: EthereumAmountUnit) {\n-        self.ewm = ewm\n-        self.identifier = identifier\n-        self.unit = unit\n-    }\n-\n-    public var hash : String {\n-        return asUTF8String (ethereumTransferGetHash (self.ewm!.core, self.identifier), true)\n-    }\n-\n-    public var sourceAddress : String {\n-        return asUTF8String (ethereumTransferGetSendAddress (self.ewm!.core, self.identifier), true)\n-    }\n-\n-    public var targetAddress : String {\n-        return asUTF8String (ethereumTransferGetRecvAddress (self.ewm!.core, self.identifier), true)\n-    }\n-\n-    public var amount : EthereumAmount {\n-        let amount : BREthereumAmount = ethereumTransferGetAmount (self.ewm!.core, self.identifier)\n-        return (AMOUNT_ETHER == amount.type\n-            ? EthereumAmount.ether(amount.u.ether.valueInWEI, unit.coreForEther)\n-            : EthereumAmount.token (amount.u.tokenQuantity.valueAsInteger,\n-                                    EthereumToken.lookup (core: amount.u.tokenQuantity.token),\n-                                    unit.coreForToken))\n-    }\n-\n-    public var fee : EthereumAmount {\n-        var overflow : Int32 = 0\n-        let fee : BREthereumEther = ethereumTransferGetFee(self.ewm!.core, self.identifier, &overflow);\n-        return EthereumAmount.ether(fee.valueInWEI, unit.coreForEther)\n-    }\n-\n-    public var confirmations : UInt64? {\n-        let confirmations = ethereumTransferGetBlockConfirmations(self.ewm!.core, self.identifier)\n-        return confirmations > 0 ? confirmations : nil\n-    }\n-\n-    public var confirmationBlockNumber : UInt64? {\n-        let number = ethereumTransferGetBlockNumber(self.ewm!.core, self.identifier)\n-        return number > 0 ? number : nil\n-    }\n-\n-    //    var gasPrice : EthereumAmount {\n-    //        let price : BREthereumAmount = ethereumTransferGetGasPriceToo (self.ewm!.core, self.identifier)\n-    //        return EthereumAmount.ether (price.u.ether.valueInWEI, WEI)\n-    //    }\n-    //\n-    //    var gasLimit : UInt64 {\n-    //        return ethereumTransactionGetGasLimit(self.ewm!.core, self.identifier)\n-    //    }\n-    //\n-    //    var gasUsed : UInt64 {\n-    //        return ethereumTransactionGetGasUsed (self.ewm!.core, self.identifier)\n-    //    }\n-    //\n-    //    var nonce : UInt64 {\n-    //        return ethereumTransactionGetNonce (self.ewm!.core, self.identifier)\n-    //    }\n-    //\n-    //    var blockNumber : UInt64 {\n-    //        return ethereumTransactionGetBlockNumber (self.ewm!.core, self.identifier)\n-    //    }\n-\n-    // State\n-}\n-\n-// MARK: - Client\n \n public enum EthereumStatus {\n     case success\n@@ -647,15 +664,21 @@ public protocol EthereumClient : class {\n                    address: String,\n                    rid: Int32) -> Void\n \n-    func saveNodes (ewm: EthereumWalletManager /* data */) -> Void\n+    func saveNodes (ewm: EthereumWalletManager,\n+                    data: Dictionary<String, String>) -> Void\n \n-    func saveBlocks (ewm: EthereumWalletManager /* data */) -> Void\n+    func saveBlocks (ewm: EthereumWalletManager,\n+                     data: Dictionary<String, String>) -> Void\n \n     func changeTransaction (ewm: EthereumWalletManager,\n-                            change: EthereumClientChangeType /* data */) -> Void\n+                            change: EthereumClientChangeType,\n+                            hash: String,\n+                            data: String) -> Void\n \n     func changeLog (ewm: EthereumWalletManager,\n-                    change: EthereumClientChangeType /* data */) -> Void\n+                    change: EthereumClientChangeType,\n+                    hash: String,\n+                    data: String) -> Void\n \n     // ...\n     func handleEWMEvent (ewm: EthereumWalletManager,\n@@ -680,6 +703,11 @@ public protocol EthereumClient : class {\n \n // MARK: - EWM\n \n+public enum EthereumKey {\n+    case paperKey (String)\n+    case publicKey (BRKey)\n+}\n+\n ///\n /// An `EthereumEWM` is a SPV/LES (Simplified Payment Verification / Light Ethereum\n /// Subprotocol) ewm in an Ethereum Network.\n@@ -723,35 +751,52 @@ public class EthereumWalletManager {\n                              network : EthereumNetwork,\n                              type: EthereumType,\n                              mode: EthereumSyncMode,\n-                             paperKey : String) {\n-        let anyClient = AnyEthereumClient (base: client)\n-        self.init (core: ethereumCreate (network.core, paperKey,\n-                                         type.core,\n-                                         mode.core,\n-                                         EthereumWalletManager.createCoreClient(client: client),\n-                                         nil,\n-                                         nil,\n-                                         nil,\n-                                         nil),\n-                   client: anyClient,\n-                   network: network)\n+                             key: EthereumKey) {\n+        self.init (client: client,\n+                   network: network,\n+                   type: type,\n+                   mode: mode,\n+                   key: key,\n+                   peers: [:],\n+                   blocks: [:],\n+                   transactions: [:],\n+                   logs: [:])\n     }\n \n-\n     public convenience init (client : EthereumClient,\n                              network : EthereumNetwork,\n                              type: EthereumType,\n                              mode: EthereumSyncMode,\n-                             publicKey : BRKey) {\n+                             key: EthereumKey,\n+                             peers: Dictionary<String,String>,\n+                             blocks: Dictionary<String,String>,\n+                             transactions: Dictionary<String,String>,\n+                             logs: Dictionary<String,String>) {\n         let anyClient = AnyEthereumClient (base: client)\n-        self.init (core: ethereumCreateWithPublicKey (network.core, publicKey,\n-                                                      type.core,\n-                                                      mode.core,\n-                                                      EthereumWalletManager.createCoreClient(client: client),\n-                                                      nil,\n-                                                      nil,\n-                                                      nil,\n-                                                      nil),\n+        var core : BREthereumEWM\n+\n+        switch key {\n+        case let .paperKey(key):\n+            core = ethereumCreate (network.core, key,\n+                                   type.core,\n+                                   mode.core,\n+                                   EthereumWalletManager.createCoreClient(client: client),\n+                                   EthereumWalletManager.asPairs (peers),\n+                                   EthereumWalletManager.asPairs (blocks),\n+                                   EthereumWalletManager.asPairs (transactions),\n+                                   EthereumWalletManager.asPairs (logs))\n+        case let .publicKey(key):\n+            core = ethereumCreateWithPublicKey (network.core, key,\n+                                                type.core,\n+                                                mode.core,\n+                                                EthereumWalletManager.createCoreClient(client: client),\n+                                                EthereumWalletManager.asPairs (peers),\n+                                                EthereumWalletManager.asPairs (blocks),\n+                                                EthereumWalletManager.asPairs (transactions),\n+                                                EthereumWalletManager.asPairs (logs))\n+        }\n+\n+        self.init (core: core,\n                    client: anyClient,\n                    network: network)\n     }\n@@ -1013,25 +1058,43 @@ public class EthereumWalletManager {\n             funcSaveNodes: { (coreClient, coreEWM, data) in\n                 if let client = coreClient.map ({ Unmanaged<AnyEthereumClient>.fromOpaque($0).takeUnretainedValue() }),\n                     let ewm = EthereumWalletManager.lookup(core: coreEWM) {\n-                    client.saveNodes(ewm: ewm)\n+                    client.saveNodes(ewm: ewm, data: EthereumWalletManager.asDictionary(data!))\n                 }},\n \n             funcSaveBlocks: { (coreClient, coreEWM, data) in\n                 if let client = coreClient.map ({ Unmanaged<AnyEthereumClient>.fromOpaque($0).takeUnretainedValue() }),\n                     let ewm = EthereumWalletManager.lookup(core: coreEWM) {\n-                    client.saveBlocks(ewm: ewm)\n+                    client.saveBlocks(ewm: ewm, data: EthereumWalletManager.asDictionary(data!))\n                 }},\n \n             funcChangeTransaction: { (coreClient, coreEWM, change, data) in\n                 if let client = coreClient.map ({ Unmanaged<AnyEthereumClient>.fromOpaque($0).takeUnretainedValue() }),\n                     let ewm = EthereumWalletManager.lookup(core: coreEWM) {\n-                    client.changeTransaction(ewm: ewm, change: EthereumClientChangeType(change))\n+\n+                    let cStrHash = ethereumHashDataPairGetHash (data)!\n+                    let cStrData = ethereumHashDataPairGetData (data)!\n+\n+                    client.changeTransaction (ewm: ewm,\n+                                              change: EthereumClientChangeType(change),\n+                                              hash: String (cString: cStrHash),\n+                                              data: String (cString: cStrData))\n+\n+                    free (cStrHash); free (cStrData)\n                 }},\n \n             funcChangeLog: { (coreClient, coreEWM, change, data) in\n                 if let client = coreClient.map ({ Unmanaged<AnyEthereumClient>.fromOpaque($0).takeUnretainedValue() }),\n                     let ewm = EthereumWalletManager.lookup(core: coreEWM) {\n-                    client.changeLog(ewm: ewm, change: EthereumClientChangeType(change))\n+\n+                    let cStrHash = ethereumHashDataPairGetHash (data)!\n+                    let cStrData = ethereumHashDataPairGetData (data)!\n+\n+                    client.changeLog (ewm: ewm,\n+                                      change: EthereumClientChangeType(change),\n+                                      hash: String (cString: cStrHash),\n+                                      data: String (cString: cStrData))\n+\n+                    free (cStrHash); free (cStrData)\n                 }},\n \n             funcEWMEvent: { (coreClient, coreEWM, event, status, message) in\n@@ -1089,6 +1152,35 @@ public class EthereumWalletManager {\n             .map { $0.value! }\n             .first { $0.core == core }\n     }\n+\n+    //\n+    // Hash Data Pair Set\n+    //\n+    private static func asPairs (_ set: Dictionary<String,String>) -> OpaquePointer {\n+        let pairs = ethereumHashDataPairSetCreate()!\n+        set.forEach { (hash: String, data: String) in\n+            ethereumHashDataPairAdd (pairs, hash, data)\n+        }\n+        return pairs\n+    }\n+\n+    private static func asDictionary (_ set: OpaquePointer) -> Dictionary<String,String> {\n+        var dict : [String:String] = [:]\n+\n+        var pair : BREthereumHashDataPair? = nil\n+        while let p = OpaquePointer.init (BRSetIterate (set, &pair)) {\n+            let cStrHash = ethereumHashDataPairGetHash (p)!\n+            let cStrData = ethereumHashDataPairGetData (p)!\n+\n+            dict [String (cString: cStrHash)] = String (cString: cStrData)\n+\n+            free (cStrHash); free (cStrData)\n+\n+            pair = p\n+        }\n+\n+        return dict\n+    }\n }\n \n // MARK: - Amount\n@@ -1289,20 +1381,28 @@ class AnyEthereumClient : EthereumClient {\n         base.getNonce(ewm: ewm, address: address, rid: rid)\n     }\n \n-    func saveNodes(ewm: EthereumWalletManager) {\n-        base.saveNodes(ewm: ewm)\n+    func saveNodes(ewm: EthereumWalletManager,\n+                   data: Dictionary<String, String>) {\n+        base.saveNodes(ewm: ewm, data: data)\n     }\n \n-    func saveBlocks(ewm: EthereumWalletManager) {\n-        base.saveBlocks(ewm: ewm)\n+    func saveBlocks(ewm: EthereumWalletManager,\n+                    data: Dictionary<String, String>) {\n+        base.saveBlocks(ewm: ewm, data: data)\n     }\n \n-    func changeTransaction(ewm: EthereumWalletManager, change: EthereumClientChangeType) {\n-        base.changeTransaction(ewm: ewm, change: change)\n+    func changeTransaction(ewm: EthereumWalletManager,\n+                           change: EthereumClientChangeType,\n+                           hash: String,\n+                           data: String) {\n+        base.changeTransaction(ewm: ewm, change: change, hash: hash, data: data)\n     }\n \n-    func changeLog(ewm: EthereumWalletManager, change: EthereumClientChangeType) {\n-        base.changeLog(ewm: ewm, change: change)\n+    func changeLog(ewm: EthereumWalletManager,\n+                   change: EthereumClientChangeType,\n+                   hash: String,\n+                   data: String) {\n+        base.changeLog (ewm: ewm, change: change, hash: hash, data: data)\n     }\n \n     func handleEWMEvent(ewm: EthereumWalletManager, event: EthereumEWMEvent) {\ndiff --git a/Swift/BRCoreTests/BRCoreTests.swift b/Swift/BRCoreTests/BRCoreTests.swift\nindex cd1e1685..db42cf27 100644\n--- a/Swift/BRCoreTests/BRCoreTests.swift\n+++ b/Swift/BRCoreTests/BRCoreTests.swift\n@@ -32,6 +32,7 @@ class BRCoreTests: XCTestCase {\n         let client = TestLightClient (network: EthereumNetwork.mainnet,\n                                       paperKey: "ginger settle marine tissue robot crane night number ramp coast roast critic")\n \n+        client.node.updateTokens()\n         client.node.connect()\n \n         sleep(1 * 60)\n@@ -63,17 +64,10 @@ class TestLightClient : EthereumClient {\n         self.node = EthereumWalletManager (client: self,\n                                            network: network,\n                                            type: EthereumType.brd,\n-                                           paperKey: paperKey)\n+                                           mode: EthereumSyncMode.blockchain,\n+                                           key: EthereumKey.paperKey (paperKey))\n     }\n \n-    //    required init(network: EthereumNetwork, publicKey: Data) {\n-    //        self.network = network\n-    //        self.node = EthereumEWM.JSON_RPC (client: self,\n-    //                                                listener: self,\n-    //                                                network: network,\n-    //                                                publicKey: publicKey)\n-    //    }\n-\n     //\n     // Client Protocol - JSON_RPC\n     //\n@@ -123,7 +117,9 @@ class TestLightClient : EthereumClient {\n     func getLogs(ewm: EthereumWalletManager, address: String, event: String, rid: Int32) {\n         ewm.announceLog(rid: rid,\n                                hash: "0xa37bd8bd8b1fa2838ef65aec9f401f56a6279f99bb1cfb81fa84e923b1b60f2b",\n-                               contract: "0x722dd3f80bac40c951b51bdd28dd19d435762180",\n+                               contract: (ewm.network == EthereumNetwork.mainnet\n+                                ? "0x3efd578b271d034a69499e4a2d933c631d44b9ad"\n+                                : "0x722dd3f80bac40c951b51bdd28dd19d435762180"),\n                                topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",\n                                         "0x0000000000000000000000000000000000000000000000000000000000000000",\n                                         "0x000000000000000000000000bdfdad139440d2db9ba2aa3b7081c2de39291508"],\n@@ -192,12 +188,20 @@ class TestLightClient : EthereumClient {\n \n         ewm.announceToken (rid: rid,\n                            address: (ewm.network == EthereumNetwork.mainnet\n-                                ? "0x9e3359f862b6c7f5c660cfd6d1aa6909b1d9504d"\n-                                : "0x6e67ccd648244b3b8e2f56149b40ba8de9d79b09"),\n+                            ? "0x9e3359f862b6c7f5c660cfd6d1aa6909b1d9504d"\n+                            : "0x6e67ccd648244b3b8e2f56149b40ba8de9d79b09"),\n                            symbol: "CCC",\n                            name: "Container Crypto Coin",\n                            description: "",\n                            decimals: 18)\n+        ewm.announceToken (rid: rid,\n+                           address: (ewm.network == EthereumNetwork.mainnet\n+                            ? "0x3efd578b271d034a69499e4a2d933c631d44b9ad"\n+                            : "0x722dd3f80bac40c951b51bdd28dd19d435762180"),\n+                           symbol: "TST",\n+                           name: "Test Standad Token",\n+                           description: "",\n+                           decimals: 18)\n     }\n \n \n@@ -207,23 +211,31 @@ class TestLightClient : EthereumClient {\n \n     func getNonce(ewm: EthereumWalletManager, address: String, rid: Int32) {\n         ewm.announceNonce(address: address, nonce: "17", rid: rid)\n-        }\n+    }\n \n-        func savePeers(ewm: EthereumWalletManager) {\n-            print ("TST: savePeers\\n")\n-        }\n+    func saveNodes (ewm: EthereumWalletManager,\n+                    data: Dictionary<String, String>) {\n+        print ("TST: savePeers\\n")\n+    }\n \n-        func saveBlocks(ewm: EthereumWalletManager) {\n-            print ("TST: saveBlocks\\n")\n-        }\n+    func saveBlocks (ewm: EthereumWalletManager,\n+                     data: Dictionary<String, String>) {\n+        print ("TST: saveBlocks\\n")\n+    }\n \n-        func changeTransaction(ewm: EthereumWalletManager, change: EthereumClientChangeType) {\n-            print ("TST: changeTransaction\\n")\n-        }\n+    func changeTransaction (ewm: EthereumWalletManager,\n+                            change: EthereumClientChangeType,\n+                            hash: String,\n+                            data: String) {\n+        print ("TST: changeTransactionXXX\\n")\n+    }\n \n-        func changeLog(ewm: EthereumWalletManager, change: EthereumClientChangeType) {\n-            print ("TST: changeLog\\n")\n-        }\n+    func changeLog (ewm: EthereumWalletManager,\n+                    change: EthereumClientChangeType,\n+                    hash: String,\n+                    data: String) {\n+        print ("TST: changeLogXXX\\n")\n+    }\n \n \n     //\n@@ -269,12 +281,5 @@ class TestLightClient : EthereumClient {\n             break;\n         }\n     }\n-\n-    func saveNodes(ewm: EthereumWalletManager) {\n-        print ("TST: SaveNodes")\n-\n-    }\n-\n-\n }\n \ndiff --git a/Swift/CoreDemo/CoreDemoEthereumClient.swift b/Swift/CoreDemo/CoreDemoEthereumClient.swift\nindex ea10a6ae..ee52a25e 100644\n--- a/Swift/CoreDemo/CoreDemoEthereumClient.swift\n+++ b/Swift/CoreDemo/CoreDemoEthereumClient.swift\n@@ -34,7 +34,7 @@ class CoreDemoEthereumClient : EthereumClient {\n                                            network: network,\n                                            type: type,\n                                            mode: mode,\n-                                           paperKey: paperKey)\n+                                           key: EthereumKey.paperKey(paperKey))\n     }\n \n     func getBalance(ewm: EthereumWalletManager, wid: EthereumWalletId, address: String, rid: Int32) {\n@@ -196,19 +196,27 @@ class CoreDemoEthereumClient : EthereumClient {\n         ewm.announceNonce(address: address, nonce: "17", rid: rid)\n     }\n \n-    func saveNodes(ewm: EthereumWalletManager) {\n+    func saveNodes(ewm: EthereumWalletManager,\n+                   data: Dictionary<String, String>) {\n         print ("TST: saveNodes")\n     }\n \n-    func saveBlocks(ewm: EthereumWalletManager) {\n+    func saveBlocks(ewm: EthereumWalletManager,\n+                    data: Dictionary<String, String>) {\n         print ("TST: saveBlocks")\n     }\n \n-    func changeTransaction(ewm: EthereumWalletManager, change: EthereumClientChangeType) {\n+    func changeTransaction (ewm: EthereumWalletManager,\n+                            change: EthereumClientChangeType,\n+                            hash: String,\n+                            data: String) {\n         print ("TST: changeTransaction")\n     }\n \n-    func changeLog(ewm: EthereumWalletManager, change: EthereumClientChangeType) {\n+    func changeLog (ewm: EthereumWalletManager,\n+                    change: EthereumClientChangeType,\n+                    hash: String,\n+                    data: String) {\n         print ("TST: changeLog")\n     }\n \ndiff --git a/ethereum/BREthereum.c b/ethereum/BREthereum.c\nindex 7a0c3736..14a5b1b4 100644\n--- a/ethereum/BREthereum.c\n+++ b/ethereum/BREthereum.c\n@@ -41,10 +41,10 @@ ethereumCreate(BREthereumNetwork network,\n                BREthereumType type,\n                BREthereumSyncMode syncMode,\n                BREthereumClient client,\n-               BRArrayOf(BREthereumPersistData) peers,\n-               BRArrayOf(BREthereumPersistData) blocks,\n-               BRArrayOf(BREthereumPersistData) transactions,\n-               BRArrayOf(BREthereumPersistData) logs) {\n+               BRSetOf(BREthereumHashDataPair) peers,\n+               BRSetOf(BREthereumHashDataPair) blocks,\n+               BRSetOf(BREthereumHashDataPair) transactions,\n+               BRSetOf(BREthereumHashDataPair) logs) {\n     return createEWM (network, createAccount(paperKey), type, syncMode, client,\n                       peers,\n                       blocks,\n@@ -58,10 +58,10 @@ ethereumCreateWithPublicKey(BREthereumNetwork network,\n                             BREthereumType type,\n                             BREthereumSyncMode syncMode,\n                             BREthereumClient client,\n-                            BRArrayOf(BREthereumPersistData) peers,\n-                            BRArrayOf(BREthereumPersistData) blocks,\n-                            BRArrayOf(BREthereumPersistData) transactions,\n-                            BRArrayOf(BREthereumPersistData) logs) {\n+                            BRSetOf(BREthereumHashDataPair) peers,\n+                            BRSetOf(BREthereumHashDataPair) blocks,\n+                            BRSetOf(BREthereumHashDataPair) transactions,\n+                            BRSetOf(BREthereumHashDataPair) logs) {\n     return createEWM (network, createAccountWithPublicKey (publicKey), type, syncMode, client,\n                       peers,\n                       blocks,\n@@ -912,3 +912,28 @@ ethereumClientAnnounceToken(BREthereumEWM ewm,\n \n     ewmClientSignalAnnounceToken (ewm, bundle, rid);\n }\n+\n+///\n+/// MARK: - Hash Data Pair\n+///\n+extern BRSetOf(BREthereumHashDataPair)\n+ethereumHashDataPairSetCreate (void) {\n+    return hashDataPairSetCreateEmpty (5);\n+}\n+\n+extern void\n+ethereumHashDataPairAdd (BRSetOf(BREthereumHashDataPair) set,\n+                         const char *hash,\n+                         const char *data) {\n+    BRSetAdd (set, hashDataPairCreateFromString(hash, data));\n+}\n+\n+extern char *\n+ethereumHashDataPairGetHash (BREthereumHashDataPair pair) {\n+    return hashDataPairGetHashAsString(pair);\n+}\n+\n+extern char *\n+ethereumHashDataPairGetData (BREthereumHashDataPair pair) {\n+    return hashDataPairGetDataAsString(pair);\n+}\ndiff --git a/ethereum/BREthereum.h b/ethereum/BREthereum.h\nindex 1dc20ca3..34c68d7c 100644\n--- a/ethereum/BREthereum.h\n+++ b/ethereum/BREthereum.h\n@@ -28,7 +28,6 @@\n \n #include <stdint.h>\n #include "BRKey.h"\n-#include "BRSet.h"\n #include "base/BREthereumBase.h"\n #include "ewm/BREthereumAmount.h"\n #include "blockchain/BREthereumNetwork.h"\n@@ -50,27 +49,6 @@ typedef int32_t BREthereumWalletId;\n typedef int32_t BREthereumBlockId;\n typedef int32_t BREthereumListenerId;\n \n-//\n-// Temporarily Here\n-//\n-typedef struct {\n-    BREthereumHash hash;\n-    BRRlpData blob;\n-} BREthereumPersistData;\n-\n-static inline size_t\n-persistDataHashValue (const void *t)\n-{\n-    return hashSetValue(&((BREthereumPersistData*) t)->hash);\n-}\n-\n-static inline int\n-persistDataHashEqual (const void *t1, const void *t2) {\n-    return t1 == t2 || hashSetEqual (&((BREthereumPersistData*) t1)->hash,\n-                                     &((BREthereumPersistData*) t2)->hash);\n-}\n-\n-\n //\n // Errors - Right Up Front - \'The Emperor Has No Clothes\' ??\n //\n@@ -183,7 +161,7 @@ typedef void\n                                      uint64_t blockNumberStop,\n                                      int rid);\n \n-    //\n+///\n // Save Sync (and other) State\n //\n typedef enum {\n@@ -199,23 +177,24 @@ typedef enum {\n typedef void\n (*BREthereumClientHandlerSaveBlocks) (BREthereumClientContext context,\n                                       BREthereumEWM ewm,\n-                                      BRArrayOf(BREthereumPersistData) persistData);\n+                                      BRSetOf(BREthereumHashDataPair) data);\n+\n typedef void\n (*BREthereumClientHandlerSaveNodes) (BREthereumClientContext context,\n                                      BREthereumEWM ewm,\n-                                     BRArrayOf(BREthereumPersistData) persistData);\n+                                     BRSetOf(BREthereumHashDataPair) data);\n \n typedef void\n (*BREthereumClientHandlerChangeTransaction) (BREthereumClientContext context,\n                                              BREthereumEWM ewm,\n                                              BREthereumClientChangeType type,\n-                                             BREthereumPersistData persistData);\n+                                             BREthereumHashDataPair data);\n \n typedef void\n (*BREthereumClientHandlerChangeLog) (BREthereumClientContext context,\n                                      BREthereumEWM ewm,\n                                      BREthereumClientChangeType type,\n-                                     BREthereumPersistData persistData);\n+                                     BREthereumHashDataPair data);\n \n //\n // Wallet Event\n@@ -407,10 +386,10 @@ ethereumCreate(BREthereumNetwork network,\n                BREthereumType type,\n                BREthereumSyncMode syncMode,\n                BREthereumClient client,\n-               BRArrayOf(BREthereumPersistData) peers,\n-               BRArrayOf(BREthereumPersistData) blocks,\n-               BRArrayOf(BREthereumPersistData) transactions,\n-               BRArrayOf(BREthereumPersistData) logs);\n+               BRSetOf(BREthereumPersistData) peers,\n+               BRSetOf(BREthereumPersistData) blocks,\n+               BRSetOf(BREthereumPersistData) transactions,\n+               BRSetOf(BREthereumPersistData) logs);\n \n /**\n  * Create a EWM managing the account associated with the publicKey.  Public key is a\n@@ -423,10 +402,10 @@ ethereumCreateWithPublicKey(BREthereumNetwork network,\n                             BREthereumType type,\n                             BREthereumSyncMode syncMode,\n                             BREthereumClient client,\n-                            BRArrayOf(BREthereumPersistData) peers,\n-                            BRArrayOf(BREthereumPersistData) blocks,\n-                            BRArrayOf(BREthereumPersistData) transactions,\n-                            BRArrayOf(BREthereumPersistData) logs);\n+                            BRSetOf(BREthereumPersistData) peers,\n+                            BRSetOf(BREthereumPersistData) blocks,\n+                            BRSetOf(BREthereumPersistData) transactions,\n+                            BRSetOf(BREthereumPersistData) logs);\n \n /**\n  * Create an Ethereum Account using `paperKey` for BIP-32 generation of keys.  The same paper key\n@@ -970,6 +949,23 @@ ethereumClientAnnounceToken(BREthereumEWM ewm,\n                             const char *strDefaultGasPrice,\n                             int rid);\n \n+//\n+// Hash Data Pair\n+//\n+extern BRSetOf(BREthereumHashDataPair)\n+ethereumHashDataPairSetCreate (void);\n+\n+extern void\n+ethereumHashDataPairAdd (BRSetOf(BREthereumHashDataPair) set,\n+                         const char *hash,\n+                         const char *data);\n+\n+extern char *\n+ethereumHashDataPairGetHash (BREthereumHashDataPair pair);\n+\n+extern char *\n+ethereumHashDataPairGetData (BREthereumHashDataPair pair);\n+\n #ifdef __cplusplus\n }\n #endif\ndiff --git a/ethereum/base/BREthereumBase.h b/ethereum/base/BREthereumBase.h\nindex d005d913..34e64122 100644\n--- a/ethereum/base/BREthereumBase.h\n+++ b/ethereum/base/BREthereumBase.h\n@@ -36,13 +36,33 @@ extern "C" {\n #define BRArrayOf(type)    type*\n #define BRSetOf(type)      BRSet*\n \n+#define FOR_SET(type,var,set) \\\n+  for (type var = BRSetIterate(set, NULL); \\\n+       NULL != var; \\\n+       var = BRSetIterate(set, var))\n \n-#include "../util/BRUtil.h"\n+typedef void\n+(*BRSetItemFree) (void *item);\n+\n+static inline void\n+BRSetFreeAll (BRSet *set, BRSetItemFree itemFree) {\n+    size_t itemsCount = BRSetCount (set);\n+    void  *itemsAll [itemsCount];\n+\n+    BRSetAll (set, itemsAll,  itemsCount);\n+    for (size_t index = 0; index < itemsCount; index++)\n+        itemFree (itemsAll[index]);\n+    BRSetClear (set);\n+    BRSetFree  (set);\n+}\n+\n+#include "../util/BRUtil.h"         // "BRInt.h"\n #include "../rlp/BRRlp.h"\n #include "BREthereumLogic.h"\n #include "BREthereumEther.h"\n #include "BREthereumGas.h"\n #include "BREthereumHash.h"\n+#include "BREthereumData.h"\n #include "BREthereumAddress.h"\n #include "BREthereumSignature.h"\n \n@@ -53,6 +73,10 @@ typedef enum {\n     RLP_TYPE_TRANSACTION_SIGNED = RLP_TYPE_NETWORK,\n } BREthereumRlpType;\n \n+\n+//\n+// Sync Mode\n+//\n typedef enum {\n     //\n     // We\'ll be willing to do a complete block chain sync, even starting at block zero.  We\'ll\ndiff --git a/ethereum/base/BREthereumData.c b/ethereum/base/BREthereumData.c\nnew file mode 100644\nindex 00000000..14747efd\n--- /dev/null\n+++ b/ethereum/base/BREthereumData.c\n@@ -0,0 +1,192 @@\n+//\n+//  BREthereumData.c\n+//  BRCore\n+//\n+//  Created by Ed Gamble on 10/3/18.\n+//  Copyright (c) 2018 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+#include "BREthereumData.h"\n+#include "../util/BRUtil.h" // encodeHexCreate()\n+\n+///\n+/// MARK: - Data\n+///\n+\n+extern BREthereumData\n+dataCreateFromBytes (size_t count, uint8_t *bytes, int takeBytes) {\n+    uint8_t *data = bytes;  // take the bytes...\n+\n+    // ... oops, we can\'t, so copy.\n+    if (!takeBytes) {\n+        data = malloc (count);\n+        memcpy (data, bytes, count);\n+    }\n+\n+    return (BREthereumData) { count, data };\n+}\n+\n+extern BREthereumData\n+dataCreateFromRlpData (BRRlpData rlp,\n+                       int takeBytes) {\n+    return dataCreateFromBytes (rlp.bytesCount, rlp.bytes, takeBytes);\n+}\n+\n+extern BREthereumData\n+dataCreateFromString (const char *string) {\n+    BREthereumData data;\n+    data.bytes = decodeHexCreate(&data.count, string, strlen (string));\n+    return data;\n+}\n+\n+extern BREthereumData\n+dataCopy (BREthereumData data) {\n+    return dataCreateFromBytes (data.count, data.bytes, 1);\n+}\n+\n+extern void\n+dataRelease (BREthereumData data) {\n+    if (NULL != data.bytes) free (data.bytes);\n+}\n+\n+extern char *\n+dataAsString (BREthereumData data) {\n+    return encodeHexCreate (NULL, data.bytes, data.count);\n+}\n+\n+extern BRRlpData\n+dataAsRlpData (BREthereumData data) {\n+    BRRlpData rlp = { data.count, malloc (data.count) };\n+    memcpy (rlp.bytes, data.bytes, data.count);\n+    return rlp;\n+}\n+\n+\n+///\n+/// MARK: - Hash Data Pair\n+///\n+\n+struct BREthereumHashDataPairRecord {\n+    BREthereumHash hash;\n+    BREthereumData data;\n+};\n+\n+extern BREthereumHashDataPair\n+hashDataPairCreate (BREthereumHash hash,\n+                    BREthereumData data) {\n+    BREthereumHashDataPair pair = malloc (sizeof (struct BREthereumHashDataPairRecord));\n+\n+    pair->hash = hash;\n+    pair->data = data;\n+\n+    return pair;\n+}\n+\n+extern BREthereumHashDataPair\n+hashDataPairCreateFromString (const char *hashString,\n+                              const char *dataString) {\n+    return hashDataPairCreate (hashCreate (hashString),\n+                               dataCreateFromString (dataString));\n+}\n+\n+extern void\n+hashDataPairRelease (BREthereumHashDataPair pair) {\n+    dataRelease (pair->data);\n+    free (pair);\n+}\n+\n+extern BREthereumHash\n+hashDataPairGetHash (BREthereumHashDataPair pair) {\n+    return pair->hash;\n+}\n+\n+// The hex-encoded hash.  You own this.\n+extern char *\n+hashDataPairGetHashAsString (BREthereumHashDataPair pair) {\n+    return hashAsString (pair->hash);\n+}\n+\n+// YOU DO NOT OWN THE RETURNED DATA\n+extern BREthereumData\n+hashDataPairGetData (BREthereumHashDataPair pair) {\n+    return pair->data;\n+}\n+\n+extern char *\n+hashDataPairGetDataAsString (BREthereumHashDataPair pair) {\n+    return dataAsString (pair->data);\n+}\n+\n+extern void\n+hashDataPairExtractStrings (BREthereumHashDataPair pair,\n+                            char **hashAsString,\n+                            char **dataAsString) {\n+    if (NULL != hashAsString) *hashAsString = hashDataPairGetHashAsString (pair);\n+    if (NULL != dataAsString) *dataAsString = hashDataPairGetDataAsString (pair);\n+}\n+\n+extern size_t\n+hashDataPairHashValue (const void *t)\n+{\n+    return hashSetValue(&((BREthereumHashDataPair) t)->hash);\n+}\n+\n+\n+extern int\n+hashDataPairHashEqual (const void *t1,\n+                       const void *t2) {\n+    return t1 == t2 || hashSetEqual (&((BREthereumHashDataPair) t1)->hash,\n+                                     &((BREthereumHashDataPair) t2)->hash);\n+}\n+\n+///\n+/// MARK: - Hash Data Pair Set\n+///\n+extern BRSetOf (BREthereumHashDataPair)\n+hashDataPairSetCreateEmpty (size_t capacity) {\n+    return BRSetNew (hashDataPairHashValue,\n+                     hashDataPairHashEqual,\n+                     capacity);\n+}\n+\n+extern BRSetOf (BREthereumHashDataPair)\n+hashDataPairSetCreate (BRArrayOf(BREthereumHashDataPair) pairs) {\n+    size_t count = array_count (pairs);\n+    BRSetOf(BREthereumHashDataPair) set = hashDataPairSetCreateEmpty(count);\n+\n+    for (size_t index = 0; index < count; index++)\n+        BRSetAdd (set, pairs[index]);\n+\n+    array_free (pairs);\n+    return set;\n+}\n+\n+extern void\n+hashDataPairSetRelease (BRSetOf (BREthereumHashDataPair) set) {\n+    size_t itemsCount = BRSetCount (set);\n+    void  *itemsAll [itemsCount];\n+\n+    BRSetAll (set, itemsAll,  itemsCount);\n+    for (size_t index = 0; index < itemsCount; index++)\n+        hashDataPairRelease ((BREthereumHashDataPair) itemsAll[index]);\n+    \n+    BRSetClear (set);\n+    BRSetFree  (set);\n+}\ndiff --git a/ethereum/base/BREthereumData.h b/ethereum/base/BREthereumData.h\nnew file mode 100644\nindex 00000000..1e7b7aed\n--- /dev/null\n+++ b/ethereum/base/BREthereumData.h\n@@ -0,0 +1,161 @@\n+//\n+//  BREthereumData.h\n+//  BRCore\n+//\n+//  Created by Ed Gamble on 10/3/18.\n+//  Copyright (c) 2018 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 BR_Ethereum_Data_H\n+#define BR_Ethereum_Data_H\n+\n+#include "BRArray.h"\n+#include "BRSet.h"\n+\n+#if !defined (BRArrayOf)\n+#define BRArrayOf(type)    type*\n+#endif\n+\n+#if !defined (BRSetOf)\n+#define BRSetOf(type)      BRSet*\n+#endif\n+\n+#include "../rlp/BRRlp.h"\n+#include "BREthereumHash.h"\n+\n+#ifdef __cplusplus\n+extern "C" {\n+#endif\n+\n+///\n+/// MARK: - Data\n+///\n+\n+typedef struct {\n+    size_t count;\n+    uint8_t *bytes;\n+} BREthereumData;\n+\n+extern BREthereumData\n+dataCreateFromBytes (size_t count,\n+                     uint8_t *bytes,\n+                     int takeBytes);\n+\n+extern BREthereumData\n+dataCreateFromRlpData (BRRlpData rlp,\n+                       int takeBytes);\n+\n+extern BREthereumData // invert dataAsString()\n+dataCreateFromString (const char *string) ;\n+\n+extern BREthereumData\n+dataCopy (BREthereumData data);\n+\n+extern void\n+dataRelease (BREthereumData data);\n+\n+// hex-encoded data; you own this.\n+extern char *\n+dataAsString (BREthereumData data);\n+\n+extern BRRlpData\n+dataAsRlpData (BREthereumData data);\n+\n+///\n+/// MARK: - Hash Data Pair\n+///\n+\n+typedef struct BREthereumHashDataPairRecord *BREthereumHashDataPair;\n+\n+/**\n+ * Create a BREthereumHashDataPair\n+ *\n+ * @note Takes ownership of `data` which must have been created with `takeBytes` (because\n+ * on `hashDataPairRelease()` the `data` will itself be released.\n+ *\n+ * @param hash the hash\n+ * @param data the data\n+ * @return the hash-data-pair\n+ */\n+extern BREthereumHashDataPair\n+hashDataPairCreate (BREthereumHash hash,\n+                    BREthereumData data);\n+\n+extern BREthereumHashDataPair\n+hashDataPairCreateFromString (const char *hashString,\n+                              const char *dataString);\n+\n+extern void\n+hashDataPairRelease (BREthereumHashDataPair pair);\n+\n+extern BREthereumHash\n+hashDataPairGetHash (BREthereumHashDataPair pair);\n+\n+// The hex-encoded hash.  You own this.\n+extern char *\n+hashDataPairGetHashAsString (BREthereumHashDataPair pair);\n+\n+// YOU DO NOT OWN THE RETURNED DATA\n+extern BREthereumData\n+hashDataPairGetData (BREthereumHashDataPair pair);\n+\n+// The hex-encoded data.  You own this.\n+extern char *\n+hashDataPairGetDataAsString (BREthereumHashDataPair pair);\n+\n+extern void\n+hashDataPairExtractStrings (BREthereumHashDataPair pair,\n+                            char **hashAsString,\n+                            char **dataAsString);\n+\n+extern size_t\n+hashDataPairHashValue (const void *t);\n+\n+extern int\n+hashDataPairHashEqual (const void *t1,\n+                       const void *t2);\n+\n+///\n+/// MARK: - Hash Data Pair Set\n+///\n+\n+extern BRSetOf (BREthereumHashDataPair)\n+hashDataPairSetCreateEmpty (size_t capacity);\n+\n+/**\n+ * Create a BRSet of BREthereumHashDataPair.\n+ *\n+ * @note This function takes ownership of `pairs` (that is, owns the individual\n+ * pair items and frees the pairs array.\n+ *\n+ * @param BREthereumHashDataPair array of pairs to add to the set.\n+ * @return a BRSET of BREthereumHashDataPair\n+ */\n+extern BRSetOf (BREthereumHashDataPair)\n+hashDataPairSetCreate (BRArrayOf(BREthereumHashDataPair) pairs);;\n+\n+extern void\n+hashDataPairSetRelease (BRSetOf (BREthereumHashDataPair) set);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* BR_Ethereum_Data_H */\ndiff --git a/ethereum/bcs/BREthereumBCS.c b/ethereum/bcs/BREthereumBCS.c\nindex 4b0723af..c167f5bf 100644\n--- a/ethereum/bcs/BREthereumBCS.c\n+++ b/ethereum/bcs/BREthereumBCS.c\n@@ -97,7 +97,7 @@ syncInterestsCreate (int count, /* BREthereumSyncInterest*/ ...) {\n  */\n static void\n bcsCreateInitializeBlocks (BREthereumBCS bcs,\n-                            BRArrayOf(BREthereumBlock) blocks) {\n+                           BRSetOf(BREthereumBlock) blocks) {\n     if (NULL == blocks) return;\n \n     bcs->chain = bcs->chainTail = NULL;\n@@ -112,10 +112,9 @@ bcsCreateInitializeBlocks (BREthereumBCS bcs,\n     //\n     // We\'ll sort `blocks` ascending by {blockNumber, timestamp}. Then we\'ll interate and chain\n     // them together while ignoring any duplicates/orphans.\n-    size_t sortedBlocksCount = array_count(blocks);\n-    BREthereumBlock *sortedBlocks;\n-    array_new(sortedBlocks, sortedBlocksCount);\n-    array_add_array(sortedBlocks, blocks, sortedBlocksCount);\n+    size_t sortedBlocksCount = BRSetCount(blocks);\n+    BREthereumBlock sortedBlocks[sortedBlocksCount];\n+    BRSetAll(blocks, (void**) sortedBlocks, sortedBlocksCount);\n \n     // TODO: Sort\n \n@@ -136,17 +135,15 @@ bcsCreateInitializeBlocks (BREthereumBCS bcs,\n             bcs->chainTail = bcs->chain;\n     }\n \n-    array_free (sortedBlocks);\n-    array_free (blocks);\n+    BRSetFree (blocks);\n }\n \n static void\n bcsCreateInitializeTransactions (BREthereumBCS bcs,\n-                                 BRArrayOf(BREthereumTransaction) transactions) {\n+                                 BRSetOf(BREthereumTransaction) transactions) {\n     if (NULL == transactions) return;\n-    \n-    for (size_t index = 0; index < array_count(transactions); index++) {\n-        BREthereumTransaction transaction = transactions[index];\n+\n+    FOR_SET (BREthereumTransaction, transaction, transactions) {\n         BREthereumTransactionStatus status = transactionGetStatus(transaction);\n \n         // For now, assume all provided transactions are in a \'final state\'.\n@@ -157,16 +154,15 @@ bcsCreateInitializeTransactions (BREthereumBCS bcs,\n         bcsSignalTransaction (bcs, transaction);\n     }\n \n-    array_free (transactions);\n+    BRSetFree (transactions);\n }\n \n static void\n bcsCreateInitializeLogs (BREthereumBCS bcs,\n-                         BRArrayOf(BREthereumLog) logs) {\n+                         BRSetOf(BREthereumLog) logs) {\n     if (NULL == logs) return;\n \n-    for (size_t index = 0; index < array_count(logs); index++) {\n-        BREthereumLog log = logs[index];\n+    FOR_SET (BREthereumLog, log, logs) {\n         BREthereumTransactionStatus status = logGetStatus(log);\n \n         // For now, assume all provided logs are in a \'final state\'.\n@@ -177,7 +173,7 @@ bcsCreateInitializeLogs (BREthereumBCS bcs,\n         bcsSignalLog (bcs, log);\n     }\n \n-    array_free (logs);\n+    BRSetFree (logs);\n }\n \n extern BREthereumBCS\n@@ -185,10 +181,10 @@ bcsCreate (BREthereumNetwork network,\n            BREthereumAddress address,\n            BREthereumBCSListener listener,\n            BREthereumSyncMode syncMode,\n-           BRArrayOf(BREthereumNodeConfig) peers,\n-           BRArrayOf(BREthereumBlock) blocks,\n-           BRArrayOf(BREthereumTransaction) transactions,\n-           BRArrayOf(BREthereumLog) logs) {\n+           BRSetOf(BREthereumNodeConfig) peers,\n+           BRSetOf(BREthereumBlock) blocks,\n+           BRSetOf(BREthereumTransaction) transactions,\n+           BRSetOf(BREthereumLog) logs) {\n            // peers\n \n     BREthereumBCS bcs = (BREthereumBCS) calloc (1, sizeof(struct BREthereumBCSStruct));\ndiff --git a/ethereum/bcs/BREthereumBCS.h b/ethereum/bcs/BREthereumBCS.h\nindex d1057c9b..05909c74 100644\n--- a/ethereum/bcs/BREthereumBCS.h\n+++ b/ethereum/bcs/BREthereumBCS.h\n@@ -158,10 +158,10 @@ bcsCreate (BREthereumNetwork network,\n            BREthereumAddress address,\n            BREthereumBCSListener listener,\n            BREthereumSyncMode syncMode,\n-           BRArrayOf(BREthereumNodeConfig) peers,\n-           BRArrayOf(BREthereumBlock) blocks,\n-           BRArrayOf(BREthereumTransaction) transactions,\n-           BRArrayOf(BREthereumLog) logs);\n+           BRSetOf(BREthereumNodeConfig) peers,\n+           BRSetOf(BREthereumBlock) blocks,\n+           BRSetOf(BREthereumTransaction) transactions,\n+           BRSetOf(BREthereumLog) logs);\n \n extern void\n bcsStart (BREthereumBCS bcs);\ndiff --git a/ethereum/bcs/BREthereumBCSPrivate.h b/ethereum/bcs/BREthereumBCSPrivate.h\nindex 1615b190..626f2279 100644\n--- a/ethereum/bcs/BREthereumBCSPrivate.h\n+++ b/ethereum/bcs/BREthereumBCSPrivate.h\n@@ -26,8 +26,6 @@\n #ifndef BR_Ethereum_BCS_Private_h\n #define BR_Ethereum_BCS_Private_h\n \n-#include "BRSet.h"\n-#include "BRArray.h"\n #include "BREthereumBCS.h"\n #include "../blockchain/BREthereumBlockChain.h"\n #include "../event/BREvent.h"\n@@ -216,11 +214,6 @@ struct BREthereumBCSStruct {\n extern const BREventType *bcsEventTypes[];\n extern const unsigned int bcsEventTypesCount;\n \n-#define FOR_SET(type,var,set) \\\n-    for (type var = BRSetIterate(set, NULL); \\\n-         NULL != var; \\\n-         var = BRSetIterate(set, var))\n-\n #define BCS_FOR_BLOCK(block)  FOR_SET(BREthereumBlock, block, bcs->blocks)\n \n #define BCS_FOR_CHAIN(bcs, block)            \\\ndiff --git a/ethereum/ewm/BREthereumEWM.c b/ethereum/ewm/BREthereumEWM.c\nindex dfb9fc31..8282c9b8 100644\n--- a/ethereum/ewm/BREthereumEWM.c\n+++ b/ethereum/ewm/BREthereumEWM.c\n@@ -50,106 +50,105 @@ ewmPeriodicDispatcher (BREventHandler handler,\n ///\n /// MARK: - Create EWM\n ///\n-static BRArrayOf(BREthereumBlock)\n-createEWMEnsureBlocks (BRArrayOf(BREthereumPersistData) blocksPersistData,\n+static BRSetOf(BREthereumBlock)\n+createEWMEnsureBlocks (BRSetOf(BREthereumHashDataPair) blocksPersistData,\n                        BREthereumNetwork network,\n                        BRRlpCoder coder) {\n-    BRArrayOf(BREthereumBlock) blocks;\n+    size_t blocksCount = (NULL == blocksPersistData ? 0 : BRSetCount (blocksPersistData));\n \n-    if (NULL == blocksPersistData || array_count(blocksPersistData) == 0) {\n-        array_new(blocks, 1);\n+    BRSetOf(BREthereumBlock) blocks = BRSetNew (blockHashValue,\n+                                                blockHashEqual,\n+                                                blocksCount);\n+\n+    if (0 == blocksCount) {\n         BREthereumBlockHeader lastCheckpointHeader = blockCheckpointCreatePartialBlockHeader(blockCheckpointLookupLatest(network));\n-        array_add(blocks, blockCreate(lastCheckpointHeader));\n+        BRSetAdd (blocks, blockCreate(lastCheckpointHeader));\n     }\n     else {\n-        array_new(blocks, array_count(blocksPersistData));\n-\n-        for (size_t index = 0; index < array_count(blocksPersistData); index++) {\n-            BRRlpItem item = rlpGetItem(coder, blocksPersistData[index].blob);\n-            BREthereumBlock block = blockRlpDecode(item, network, RLP_TYPE_ARCHIVE, coder);\n+        FOR_SET (BREthereumHashDataPair, pair, blocksPersistData) {\n+            BRRlpItem item = rlpGetItem (coder, dataAsRlpData (hashDataPairGetData (pair)));\n+            BREthereumBlock block = blockRlpDecode (item, network, RLP_TYPE_ARCHIVE, coder);\n             rlpReleaseItem(coder, item);\n-            rlpDataRelease(blocksPersistData[index].blob);\n-            array_insert (blocks, index, block);\n+            BRSetAdd (blocks, block);\n         }\n-    }\n \n-    if (NULL != blocksPersistData) {\n-        array_free(blocksPersistData);\n+        if (NULL != blocksPersistData)\n+            hashDataPairSetRelease(blocksPersistData);\n     }\n \n     return blocks;\n }\n \n-static BRArrayOf(BREthereumNodeConfig)\n-createEWMEnsureNodes (BRArrayOf(BREthereumPersistData) nodesPersistData,\n+static BRSetOf(BREthereumNodeConfig)\n+createEWMEnsureNodes (BRSetOf(BREthereumHashDataPair) nodesPersistData,\n                       BRRlpCoder coder) {\n-    BRArrayOf(BREthereumNodeConfig) nodes;\n \n-    size_t peersCount = (NULL == nodesPersistData ? 0 : array_count(nodesPersistData));\n-    array_new(nodes, peersCount);\n-\n-    for (size_t index = 0; index < peersCount; index++) {\n-        BRRlpItem item = rlpGetItem(coder, nodesPersistData[index].blob);\n-        BREthereumNodeConfig node = nodeConfigDecode(item, coder);\n-        rlpReleaseItem(coder, item);\n-        rlpDataRelease(nodesPersistData[index].blob);\n-        array_insert (nodes, index, node);\n-    }\n+    BRSetOf (BREthereumNodeConfig) nodes =\n+    BRSetNew (nodeConfigHashValue,\n+              nodeConfigHashEqual,\n+              (NULL == nodesPersistData ? 0 : BRSetCount(nodesPersistData)));\n \n     if (NULL != nodesPersistData) {\n-        array_free (nodesPersistData);\n+        FOR_SET (BREthereumHashDataPair, pair, nodesPersistData) {\n+            BRRlpItem item = rlpGetItem (coder, dataAsRlpData (hashDataPairGetData (pair)));\n+            BREthereumNodeConfig node = nodeConfigDecode(item, coder);\n+            rlpReleaseItem(coder, item);\n+            BRSetAdd (nodes, node);\n+        }\n+\n+        hashDataPairSetRelease(nodesPersistData);\n     }\n \n     return nodes;\n }\n \n-static BRArrayOf(BREthereumTransaction)\n-createEWMEnsureTransactions (BRArrayOf(BREthereumPersistData) transactionsPersistData,\n+static BRSetOf(BREthereumTransaction)\n+createEWMEnsureTransactions (BRSetOf(BREthereumHashDataPair) transactionsPersistData,\n                              BREthereumNetwork network,\n                              BRRlpCoder coder) {\n-    BRArrayOf(BREthereumTransaction) transactions;\n+    BRSetOf(BREthereumTransaction) transactions =\n+    BRSetNew (transactionHashValue,\n+              transactionHashEqual,\n+              (NULL == transactionsPersistData ? 0 : BRSetCount (transactionsPersistData)));\n \n-    size_t transactionsCount = (NULL == transactionsPersistData ? 0 : array_count(transactionsPersistData));\n-    array_new(transactions, transactionsCount);\n+    if (NULL != transactionsPersistData) {\n+        FOR_SET (BREthereumHashDataPair, pair, transactionsPersistData) {\n+            fprintf (stdout, "ETH: TST: EnsureTrans @ %p\\n", hashDataPairGetData(pair).bytes);\n \n-    for (size_t index = 0; index < transactionsCount; index++) {\n-        fprintf (stdout, "ETH: TST: EnsureTrans @ %p\\n", transactionsPersistData[index].blob.bytes);\n+            BRRlpItem item = rlpGetItem (coder, dataAsRlpData (hashDataPairGetData (pair)));\n+            BREthereumTransaction transaction = transactionRlpDecode(item, network, RLP_TYPE_ARCHIVE, coder);\n+            rlpReleaseItem (coder, item);\n+            BRSetAdd (transactions, transaction);\n+        }\n \n-        BRRlpItem item = rlpGetItem(coder, transactionsPersistData[index].blob);\n-        BREthereumTransaction transaction = transactionRlpDecode(item, network, RLP_TYPE_ARCHIVE, coder);\n-        rlpReleaseItem(coder, item);\n-        rlpDataRelease(transactionsPersistData[index].blob);\n-        array_insert (transactions, index, transaction);\n-    }\n-\n-    if (NULL != transactionsPersistData) {\n-        array_free(transactionsPersistData);\n+        hashDataPairSetRelease(transactionsPersistData);\n     }\n \n     return transactions;\n }\n \n-static BRArrayOf(BREthereumLog)\n-createEWMEnsureLogs(BRArrayOf(BREthereumPersistData) logsPersistData,\n+static BRSetOf(BREthereumLog)\n+createEWMEnsureLogs(BRSetOf(BREthereumHashDataPair) logsPersistData,\n                     BREthereumNetwork network,\n                     BRRlpCoder coder) {\n-    BRArrayOf(BREthereumLog) logs;\n+    BRSetOf(BREthereumLog) logs =\n+    BRSetNew (logHashValue,\n+              logHashEqual,\n+              (NULL == logsPersistData ? 0 : BRSetCount(logsPersistData)));\n \n-    size_t logsCount = (NULL == logsPersistData ? 0 : array_count(logsPersistData));\n-    array_new(logs, logsCount);\n+    if (NULL != logsPersistData) {\n \n-    for (size_t index = 0; index < logsCount; index++) {\n-        fprintf (stdout, "ETH: TST: EnsureLogs @ %p\\n", logsPersistData[index].blob.bytes);\n+        FOR_SET (BREthereumHashDataPair, pair, logsPersistData) {\n+            fprintf (stdout, "ETH: TST: EnsureLogs @ %p\\n", hashDataPairGetData(pair).bytes);\n+            BRRlpItem item = rlpGetItem (coder, dataAsRlpData (hashDataPairGetData (pair)));\n \n-        BRRlpItem item = rlpGetItem(coder, logsPersistData[index].blob);\n-        BREthereumLog log = logRlpDecode(item, RLP_TYPE_ARCHIVE, coder);\n-        rlpReleaseItem(coder, item);\n-        rlpDataRelease(logsPersistData[index].blob);\n-        array_insert (logs, index, log);\n-    }\n+            BREthereumLog log = logRlpDecode(item, RLP_TYPE_ARCHIVE, coder);\n+            rlpReleaseItem(coder, item);\n \n-    if (NULL != logsPersistData) {\n-        array_free(logsPersistData);\n+            BRSetAdd (logs, log);\n+        }\n+\n+        hashDataPairSetRelease (logsPersistData);\n     }\n \n     return logs;\n@@ -162,10 +161,10 @@ createEWM (BREthereumNetwork network,\n            // serialized: headers, transactions, logs\n            BREthereumSyncMode syncMode,\n            BREthereumClient client,\n-           BRArrayOf(BREthereumPersistData) nodesPersistData,\n-           BRArrayOf(BREthereumPersistData) blocksPersistData,\n-           BRArrayOf(BREthereumPersistData) transactionsPersistData,\n-           BRArrayOf(BREthereumPersistData) logsPersistData) {\n+           BRSetOf(BREthereumHashDataPair) nodesPersistData,\n+           BRSetOf(BREthereumHashDataPair) blocksPersistData,\n+           BRSetOf(BREthereumHashDataPair) transactionsPersistData,\n+           BRSetOf(BREthereumHashDataPair) logsPersistData) {\n     BREthereumEWM ewm = (BREthereumEWM) calloc (1, sizeof (struct BREthereumEWMRecord));\n \n     ewm->state = LIGHT_NODE_CREATED;\n@@ -214,10 +213,10 @@ createEWM (BREthereumNetwork network,\n \n     // Extract the provided persist data.  We\'ll use these when configuring BCS (if EWM_USE_LES)\n     // or EWM (if EWM_USE_BRD).\n-    BRArrayOf(BREthereumNodeConfig)  nodes        = createEWMEnsureNodes(nodesPersistData, ewm->coder);\n-    BRArrayOf(BREthereumBlock)       blocks       = createEWMEnsureBlocks (blocksPersistData, network, ewm->coder);\n-    BRArrayOf(BREthereumTransaction) transactions = createEWMEnsureTransactions(transactionsPersistData, network, ewm->coder);\n-    BRArrayOf(BREthereumLog)         logs         = createEWMEnsureLogs(logsPersistData, network, ewm->coder);\n+    BRSetOf(BREthereumNodeConfig)  nodes        = createEWMEnsureNodes(nodesPersistData, ewm->coder);\n+    BRSetOf(BREthereumBlock)       blocks       = createEWMEnsureBlocks (blocksPersistData, network, ewm->coder);\n+    BRSetOf(BREthereumTransaction) transactions = createEWMEnsureTransactions(transactionsPersistData, network, ewm->coder);\n+    BRSetOf(BREthereumLog)         logs         = createEWMEnsureLogs(logsPersistData, network, ewm->coder);\n \n     // Support the requested type\n     switch (ewm->type) {\n@@ -252,29 +251,30 @@ createEWM (BREthereumNetwork network,\n \n         case EWM_USE_BRD: {\n             // Announce all the provided transactions...\x01\n-            for (size_t index = 0; index < array_count(transactions); index++)\n-                ewmSignalTransaction (ewm, BCS_CALLBACK_TRANSACTION_ADDED, transactions[index]);\n+            FOR_SET (BREthereumTransaction, transaction, transactions)\n+                ewmSignalTransaction (ewm, BCS_CALLBACK_TRANSACTION_ADDED, transaction);\n \n             // ... as well as the provided logs...\n-            for (size_t index = 0; index < array_count(logs); index++)\n-                ewmSignalLog (ewm, BCS_CALLBACK_LOG_ADDED, logs[index]);\n+            FOR_SET (BREthereumLog, log, logs)\n+                ewmSignalLog (ewm, BCS_CALLBACK_LOG_ADDED, log);\n \n             // ... and then the latest block.\n-            BREthereumBlock block = NULL;\n-            for (size_t index = 0; index < array_count(blocks); index++)\n-                if (NULL == block || blockGetNumber(block) < blockGetNumber(blocks[index]))\n-                    block = blocks[index];\n+            BREthereumBlock lastBlock = NULL;\n+            FOR_SET (BREthereumBlock, block, blocks)\n+                if (NULL == lastBlock || blockGetNumber(lastBlock) < blockGetNumber(block))\n+                    lastBlock = block;\n             ewmSignalBlockChain (ewm,\n-                                 blockGetHash(block),\n-                                 blockGetNumber(block),\n-                                 blockGetTimestamp(block));\n+                                 blockGetHash( lastBlock),\n+                                 blockGetNumber (lastBlock),\n+                                 blockGetTimestamp (lastBlock));\n \n             // ... and then ignore nodes\n \n-            array_free (nodes);\n-            array_free (blocks);\n-            array_free(transactions);\n-            array_free(logs);\n+            // TODO: What items to free?\n+            BRSetFree (nodes);\n+            BRSetFree (blocks);\n+            BRSetFree (transactions);\n+            BRSetFree (logs);\n \n             // Add ewmPeriodicDispatcher to handlerForMain.\n             eventHandlerSetTimeoutDispatcher(ewm->handlerForMain,\n@@ -888,8 +888,6 @@ extern void\n ewmHandleTransaction (BREthereumEWM ewm,\n                       BREthereumBCSCallbackTransactionType type,\n                       BREthereumTransaction transaction) {\n-    \n-\n     BREthereumHash hash = transactionGetHash(transaction);\n \n     BREthereumHashString hashString;\n@@ -988,16 +986,14 @@ ewmHandleSaveBlocks (BREthereumEWM ewm,\n                      BRArrayOf(BREthereumBlock) blocks) {\n     eth_log("EWM", "Save Blocks: %zu", array_count(blocks));\n \n-    BRArrayOf(BREthereumPersistData) blocksToSave;\n-    array_new(blocksToSave, array_count(blocks));\n+    BRSetOf(BREthereumHashDataPair) blocksToSave = hashDataPairSetCreateEmpty (array_count (blocks));\n+\n     for (size_t index = 0; index < array_count(blocks); index++) {\n         BRRlpItem item = blockRlpEncode(blocks[index], ewm->network, RLP_TYPE_ARCHIVE, ewm->coder);\n-        BREthereumPersistData persistData = {\n-            blockGetHash(blocks[index]),\n-            rlpGetData(ewm->coder, item)\n-        };\n+        BRSetAdd (blocksToSave,\n+                  hashDataPairCreate (blockGetHash(blocks[index]),\n+                                      dataCreateFromRlpData (rlpGetData (ewm->coder, item), 1)));\n         rlpReleaseItem(ewm->coder, item);\n-        array_add (blocksToSave, persistData);\n     }\n \n     // TODO: ewmSignalSaveBlocks(ewm, blocks)\n@@ -1013,17 +1009,14 @@ ewmHandleSaveNodes (BREthereumEWM ewm,\n                     BRArrayOf(BREthereumNodeConfig) nodes) {\n     size_t nodesCount = array_count(nodes);\n \n-    // Serialize BREthereumPeerConfig\n-    BRArrayOf(BREthereumPersistData) nodesToSave;\n-    array_new(nodesToSave, 0);\n+    BRSetOf(BREthereumHashDataPair) nodesToSave = hashDataPairSetCreateEmpty (array_count (nodes));\n+\n     for (size_t index = 0; index < array_count(nodes); index++) {\n         BRRlpItem item = nodeConfigEncode(nodes[index], ewm->coder);\n-        BREthereumPersistData persistData = {\n-            nodeConfigGetHash(nodes[index]),\n-            rlpGetData (ewm->coder, item)\n-        };\n+        BRSetAdd (nodesToSave,\n+                  hashDataPairCreate (nodeConfigGetHash(nodes[index]),\n+                                      dataCreateFromRlpData (rlpGetData (ewm->coder, item), 1)));\n         rlpReleaseItem (ewm->coder, item);\n-        array_add (nodesToSave, persistData);\n     }\n \n     // TODO: ewmSignalSavenodes(ewm, nodes);\ndiff --git a/ethereum/ewm/BREthereumEWM.h b/ethereum/ewm/BREthereumEWM.h\nindex ae5fc7bd..857fa3a9 100644\n--- a/ethereum/ewm/BREthereumEWM.h\n+++ b/ethereum/ewm/BREthereumEWM.h\n@@ -44,10 +44,10 @@ createEWM (BREthereumNetwork network,\n            BREthereumType type,\n            BREthereumSyncMode syncMode,\n            BREthereumClient client,\n-           BRArrayOf(BREthereumPersistData) peers,\n-           BRArrayOf(BREthereumPersistData) blocks,\n-           BRArrayOf(BREthereumPersistData) transactions,\n-           BRArrayOf(BREthereumPersistData) logs);\n+           BRSetOf(BREthereumPersistData) peers,\n+           BRSetOf(BREthereumPersistData) blocks,\n+           BRSetOf(BREthereumPersistData) transactions,\n+           BRSetOf(BREthereumPersistData) logs);\n \n extern void\n ewmDestroy (BREthereumEWM ewm);\ndiff --git a/ethereum/ewm/BREthereumEWMClient.c b/ethereum/ewm/BREthereumEWMClient.c\nindex 72327ee3..bfc19a83 100644\n--- a/ethereum/ewm/BREthereumEWMClient.c\n+++ b/ethereum/ewm/BREthereumEWMClient.c\n@@ -618,13 +618,14 @@ ewmClientHandleTransferEvent (BREthereumEWM ewm,\n                                ? transactionGetHash(transaction)\n                                : logGetHash(log));\n \n-        BREthereumPersistData persistData = { hash,  rlpGetData(ewm->coder, item) };\n+        BREthereumHashDataPair data = hashDataPairCreate (hash,\n+                                                          dataCreateFromRlpData(rlpGetData(ewm->coder, item), 1));\n         rlpReleaseItem(ewm->coder, item);\n \n         if (NULL != transaction)\n-            ewm->client.funcChangeTransaction (ewm->client.context, ewm, type, persistData);\n+            ewm->client.funcChangeTransaction (ewm->client.context, ewm, type, data);\n         else\n-            ewm->client.funcChangeLog (ewm->client.context, ewm, type, persistData);\n+            ewm->client.funcChangeLog (ewm->client.context, ewm, type, data);\n     }\n \n     ewm->client.funcTransferEvent (ewm->client.context,\ndiff --git a/ethereum/ewm/testEwm.c b/ethereum/ewm/testEwm.c\nindex a17e1e99..459766de 100644\n--- a/ethereum/ewm/testEwm.c\n+++ b/ethereum/ewm/testEwm.c\n@@ -264,7 +264,7 @@ clientGetBlocks (BREthereumClientContext context,\n             blockNumbers[index - 1] > blockNumberStop)\n             array_rm (blockNumbers, index - 1);\n \n-    ethereumClientAnnounceBlocks (ewm, rid, array_count(blockNumbers), blockNumbers);\n+    ethereumClientAnnounceBlocks (ewm, rid, (int) array_count(blockNumbers), blockNumbers);\n }\n \n static void\n@@ -343,81 +343,70 @@ clientGetTokens (BREthereumClientContext context,\n //\n // Save Blocks\n //\n-BRArrayOf(BREthereumPersistData) savedBlocks = NULL;\n+BRSetOf(BREthereumHashDataPair) savedBlocks = NULL;\n \n static void\n clientSaveBlocks (BREthereumClientContext context,\n                   BREthereumEWM ewm,\n-                  BRArrayOf(BREthereumPersistData) blocksToSave) {\n+                  BRSetOf(BREthereumHashDataPair) blocksToSave) {\n     static int count = 0;\n     static int total = 0;\n-    total += array_count(blocksToSave);\n+    total += BRSetCount(blocksToSave);\n     eth_log("TST", "Save Blocks (%d): %d", (++count), total);\n \n-    if (NULL != savedBlocks) {\n-        for (size_t item = 0; item < array_count(savedBlocks); item++)\n-            rlpDataRelease(savedBlocks[item].blob);\n-        array_free(savedBlocks);\n-        savedBlocks = NULL;\n-    }\n-\n-    array_new (savedBlocks, array_count(blocksToSave));\n-    for (size_t index = 0; index < array_count(blocksToSave); index++)\n-        array_add (savedBlocks, blocksToSave[index]);\n+    if (NULL == savedBlocks)\n+        savedBlocks = hashDataPairSetCreateEmpty (BRSetCount(blocksToSave));\n \n-    array_free (blocksToSave);\n+    BRSetUnion (savedBlocks, blocksToSave);\n+    BRSetFree (blocksToSave);\n }\n \n //\n // Save Peers\n //\n-BRArrayOf(BREthereumPersistData) savedNodes = NULL;\n+BRSetOf(BREthereumHashDataPair) savedNodes = NULL;\n \n static void\n clientSaveNodes (BREthereumClientContext context,\n                  BREthereumEWM ewm,\n-                 BRArrayOf(BREthereumPersistData) nodesToSave) {\n-    if (NULL != savedNodes) {\n-        for (size_t item = 0; item < array_count(savedNodes); item++)\n-            rlpDataRelease(savedNodes[item].blob);\n-        array_free(savedNodes);\n-        savedNodes = NULL;\n-    }\n-    savedNodes = nodesToSave;\n+                 BRSetOf(BREthereumHashDataPair) nodesToSave) {\n+    if (NULL != savedNodes)\n+        savedNodes = hashDataPairSetCreateEmpty (BRSetCount(nodesToSave));\n \n+    BRSetUnion (savedNodes, nodesToSave);\n+    BRSetFree (nodesToSave);\n }\n \n //\n // Update Transactions\n //\n-BRSetOf(BREthereumPersistData) savedTransactions = NULL;\n+BRSetOf(BREthereumHashDataPair) savedTransactions = NULL;\n \n static void\n clientUpdateTransaction (BREthereumClientContext context,\n                          BREthereumEWM ewm,\n                          BREthereumClientChangeType type,\n-                         BREthereumPersistData transactionPersistData) {\n-    fprintf (stdout, "ETH: TST: UpdateTransaction: ev=%s @ %p\\n", CLIENT_CHANGE_TYPE_NAME(type), transactionPersistData.blob.bytes);\n+                         BREthereumHashDataPair data) {\n+    fprintf (stdout, "ETH: TST: UpdateTransaction: ev=%s @ %p\\n",\n+             CLIENT_CHANGE_TYPE_NAME(type),\n+             hashDataPairGetData(data).bytes);\n \n     if (NULL == savedTransactions)\n-        savedTransactions = BRSetNew(persistDataHashValue, persistDataHashEqual, 100);\n-\n-    BREthereumPersistData *data = malloc (sizeof (BREthereumPersistData));\n-    memcpy (data, &transactionPersistData, sizeof (BREthereumPersistData));\n+        savedTransactions = hashDataPairSetCreateEmpty (100);\n \n     switch (type) {\n         case CLIENT_CHANGE_ADD:\n-            BRSetAdd(savedTransactions, data);\n+            BRSetAdd (savedTransactions, data);\n             break;\n \n         case CLIENT_CHANGE_REM:\n             data = BRSetRemove(savedTransactions, data);\n-            if (NULL != data) { rlpDataRelease(data->blob); free (data); }\n+            if (NULL != data) hashDataPairRelease(data);\n             break;\n \n         case CLIENT_CHANGE_UPD:\n             data = BRSetAdd(savedTransactions, data);\n-            if (NULL != data) { rlpDataRelease(data->blob); free (data); }\n+            if (NULL != data) hashDataPairRelease(data);\n             break;\n     }\n }\n@@ -431,28 +420,27 @@ static void\n clientUpdateLog (BREthereumClientContext context,\n                  BREthereumEWM ewm,\n                  BREthereumClientChangeType type,\n-                 BREthereumPersistData logPersistData) {\n-    fprintf (stdout, "ETH: TST: UpdateLog: ev=%s @ %p\\n", CLIENT_CHANGE_TYPE_NAME(type), logPersistData.blob.bytes);\n+                 BREthereumHashDataPair data) {\n+    fprintf (stdout, "ETH: TST: UpdateLog: ev=%s @ %p\\n",\n+             CLIENT_CHANGE_TYPE_NAME(type),\n+             hashDataPairGetData(data).bytes);\n \n-    if (NULL == savedLogs)\n-        savedLogs = BRSetNew(persistDataHashValue, persistDataHashEqual, 100);\n-\n-    BREthereumPersistData *data = malloc (sizeof (BREthereumPersistData));\n-    memcpy (data, &logPersistData, sizeof (BREthereumPersistData));\n+    if (NULL == savedTransactions)\n+        savedTransactions = hashDataPairSetCreateEmpty (100);\n \n     switch (type) {\n         case CLIENT_CHANGE_ADD:\n-            BRSetAdd(savedLogs, data);\n+            BRSetAdd (savedTransactions, data);\n             break;\n \n         case CLIENT_CHANGE_REM:\n-            data = BRSetRemove(savedLogs, data);\n-            if (NULL != data) { rlpDataRelease(data->blob); free (data); }\n+            data = BRSetRemove(savedTransactions, data);\n+            if (NULL != data) hashDataPairRelease(data);\n             break;\n \n         case CLIENT_CHANGE_UPD:\n-            data = BRSetAdd(savedLogs, data);\n-            if (NULL != data) { rlpDataRelease(data->blob); free (data); }\n+            data = BRSetAdd(savedTransactions, data);\n+            if (NULL != data) hashDataPairRelease(data);\n             break;\n     }\n }\n@@ -782,28 +770,20 @@ runSyncTest (BREthereumType type,\n     char *paperKey = "0xb302B06FDB1348915599D21BD54A06832637E5E8";\n     alarmClockCreateIfNecessary (1);\n \n-    BRArrayOf(BREthereumPersistData) blocks = (restart ? savedBlocks : NULL);\n-    BRArrayOf(BREthereumPersistData) nodes = (restart ? savedNodes : NULL);\n-    BRArrayOf(BREthereumPersistData) transactions = NULL;\n-    BRArrayOf(BREthereumPersistData) logs = NULL;\n+    BRSetOf(BREthereumHashDataPair) blocks = (restart ? savedBlocks : NULL);\n+    BRSetOf(BREthereumHashDataPair) nodes = (restart ? savedNodes : NULL);\n+    BRSetOf(BREthereumHashDataPair) transactions = NULL;\n+    BRSetOf(BREthereumHashDataPair) logs = NULL;\n \n     if (restart) {\n         if (NULL != savedTransactions) {\n-            size_t transactionsCount = BRSetCount(savedTransactions);\n-            array_new(transactions, transactionsCount);\n-            for (BREthereumPersistData *data = BRSetIterate(savedTransactions, NULL);\n-                 NULL != data;\n-                 data = BRSetIterate(savedTransactions, data))\n-                array_add (transactions, *data);\n+            transactions = BRSetNew (transactionHashValue, transactionHashEqual, BRSetCount (savedTransactions));\n+            BRSetUnion (transactions, savedTransactions); // copy\n         }\n \n         if (NULL != savedLogs) {\n-            size_t logsCount = BRSetCount(savedLogs);\n-            array_new(logs, logsCount);\n-            for (BREthereumPersistData *data = BRSetIterate(savedLogs, NULL);\n-                 NULL != data;\n-                 data = BRSetIterate(savedLogs, data))\n-                array_add (logs, *data);\n+            logs = BRSetNew (logHashValue, logHashEqual, BRSetCount(savedLogs));\n+            BRSetUnion (logs, savedLogs); // copy\n         }\n     }\n \ndiff --git a/ethereum/les/BREthereumLES.c b/ethereum/les/BREthereumLES.c\nindex e27f7d3e..e766dfc5 100644\n--- a/ethereum/les/BREthereumLES.c\n+++ b/ethereum/les/BREthereumLES.c\n@@ -196,6 +196,19 @@ nodeConfigCreateEndpoint (BREthereumNodeConfig config) {\n     return nodeEndpointCreate ((BREthereumDISNeighbor) { config->endpoint, config->key });\n }\n \n+extern size_t\n+nodeConfigHashValue (const void *t)\n+{\n+    return hashSetValue(&((BREthereumNodeConfig) t)->hash);\n+}\n+\n+extern int\n+nodeConfigHashEqual (const void *t1, const void *t2) {\n+    return t1 == t2 || hashSetEqual (&((BREthereumNodeConfig) t1)->hash,\n+                                     &((BREthereumNodeConfig) t2)->hash);\n+}\n+\n+\n /**\n  * A LES Request is a LES Message with associated callbacks.  We\'ll send the message (once we have\n  * connected to a LES node) and then wait for a response with the corresponding `requestId`.  Once\n@@ -395,7 +408,7 @@ lesCreate (BREthereumNetwork network,\n            uint64_t headNumber,\n            UInt256 headTotalDifficulty,\n            BREthereumHash genesisHash,\n-           BRArrayOf(BREthereumNodeConfig) configs) {\n+           BRSetOf(BREthereumNodeConfig) configs) {\n     \n     BREthereumLES les = (BREthereumLES) calloc (1, sizeof(struct BREthereumLESRecord));\n     assert (NULL != les);\n@@ -495,8 +508,8 @@ lesCreate (BREthereumNetwork network,\n         BRArrayOf(BREthereumNodeEndpoint) preferredEndpoints;\n         array_new (preferredEndpoints, 5);\n \n-        for (size_t index = 0; index < array_count(configs); index++) {\n-            BREthereumNodeEndpoint endpoint = nodeConfigCreateEndpoint(configs[index]);\n+        FOR_SET (BREthereumNodeConfig, config, configs) {\n+            BREthereumNodeEndpoint endpoint = nodeConfigCreateEndpoint(config);\n \n             if (nodeEndpointIsPreferred (&endpoint, bootstrapLCLEnodes) ||\n                 nodeEndpointIsPreferred (&endpoint, bootstrapBRDEnodes))\n@@ -504,7 +517,7 @@ lesCreate (BREthereumNetwork network,\n             else\n                 lesEnsureNodeForEndpoint (les,\n                                           endpoint,\n-                                          configs[index]->state,\n+                                          config->state,\n                                           ETHEREUM_BOOLEAN_FALSE,\n                                           NULL);\n         }\ndiff --git a/ethereum/les/BREthereumLES.h b/ethereum/les/BREthereumLES.h\nindex a4f1aaa4..0717f47e 100644\n--- a/ethereum/les/BREthereumLES.h\n+++ b/ethereum/les/BREthereumLES.h\n@@ -60,6 +60,15 @@ extern BREthereumNodeConfig\n nodeConfigDecode (BRRlpItem item,\n                   BRRlpCoder coder);\n \n+// Support BRSet\n+extern size_t\n+nodeConfigHashValue (const void *h);\n+\n+// Support BRSet\n+extern int\n+nodeConfigHashEqual (const void *h1, const void *h2);\n+\n+\n /*!\n  * @typedef BREthereumLES\n  *\n@@ -158,7 +167,7 @@ lesCreate (BREthereumNetwork network,\n            uint64_t headNumber,\n            UInt256 headTotalDifficulty,\n            BREthereumHash genesisHash,\n-           BRArrayOf(BREthereumNodeConfig) configs);\n+           BRSetOf(BREthereumNodeConfig) configs);\n \n /*!\n  * @function lesRelease\ndiff --git a/ethereum/les/BREthereumNode.c b/ethereum/les/BREthereumNode.c\nindex b955f42e..956472b7 100644\n--- a/ethereum/les/BREthereumNode.c\n+++ b/ethereum/les/BREthereumNode.c\n@@ -1790,6 +1790,7 @@ nodeSetStateInitial (BREthereumNode node,\n                         case NODE_PROTOCOL_CAPABILITIES_MISMATCH:\n                         case NODE_PROTOCOL_STATUS_MISMATCH:\n                         case NODE_PROTOCOL_UDP_EXCESSIVE_BYTE_COUNT:\n+                        case NODE_PROTOCOL_RLP_PARSE:\n                             node->states[route] = state; // no recover; adopt the PROTOCOL error.\n                             break;\n \ndiff --git a/ethereum/rlp/BRRlpCoder.c b/ethereum/rlp/BRRlpCoder.c\nindex 3e2825b0..f550536a 100644\n--- a/ethereum/rlp/BRRlpCoder.c\n+++ b/ethereum/rlp/BRRlpCoder.c\n@@ -823,14 +823,17 @@ rlpDecodeList (BRRlpCoder coder, BRRlpItem item, size_t *itemsCount) {\n }\n \n //\n-// Data\n+// RLP Data\n //\n extern BRRlpData\n-createRlpDataEmpty (void) {\n-    BRRlpData data;\n-    data.bytesCount = 0;\n-    data.bytes = NULL;\n-    return data;\n+rlpDataCopy (BRRlpData data) {\n+    uint8_t *bytesCopy = NULL;\n+    if (NULL != data.bytes) {\n+        bytesCopy = malloc (data.bytesCount);\n+        memcpy (bytesCopy, data.bytes, data.bytesCount);\n+    }\n+\n+    return (BRRlpData) { data.bytesCount, bytesCopy };\n }\n \n extern void\ndiff --git a/ethereum/rlp/BRRlpCoder.h b/ethereum/rlp/BRRlpCoder.h\nindex 557b3283..469abb86 100644\n--- a/ethereum/rlp/BRRlpCoder.h\n+++ b/ethereum/rlp/BRRlpCoder.h\n@@ -63,7 +63,7 @@ typedef struct {\n } BRRlpData;\n \n extern BRRlpData\n-createRlpDataEmpty (void);\n+rlpDataCopy (BRRlpData data);\n \n extern void\n rlpDataRelease (BRRlpData data);\n', 'commit_subject': "Merge branch 'feature/CORE-149' into ethereum", 'repo_name': 'breadwallet/breadwallet-core', 'committer': {'name': 'Ed Gamble', 'email': 'a162caffcb6897220f4d1cf28164d73cb91993a8@breadwallet.com', 'time_sec': '1538690821', 'tz_offset': '-420', 'date': {'seconds': '1538690821'}}, 'commit_tree': 'a0e623cd5de88a8aa79b264ee1b25a8e043776b0', 'commit_message': "Merge branch 'feature/CORE-149' into ethereum\n", 'language_name': 'C++', 'license': 'mit', 'hunks': '@@ -63,7 +63,7 @@ typedef struct {\n } BRRlpData;\n \n extern BRRlpData\n-createRlpDataEmpty (void);\n+rlpDataCopy (BRRlpData data);\n \n extern void\n rlpDataRelease (BRRlpData data);\n', 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/ethereum/rlp/BRRlpCoder.h', 'tgt_file': 'b/ethereum/rlp/BRRlpCoder.h', 'file_extension': '.h', 'before_file': '//\n//  rlp\n//  Core Ethereum\n//\n//  Created by Ed Gamble on 2/25/18.\n//  Copyright © 2018-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 BR_RLP_Coder_H\n#define BR_RLP_Coder_H\n\n#include <stddef.h>\n#include <stdint.h>\n#include "support/BRInt.h"\n\n#ifdef __cplusplus\nextern "C" {\n#endif\n\n//\n// RLP Coder\n//\ntypedef struct BRRlpCoderRecord *BRRlpCoder;\n\nextern BRRlpCoder\nrlpCoderCreate (void);\n\nextern void\nrlpCoderRelease (BRRlpCoder coder);\n\n/**\n * Reclaim coder memory. A coder can hold memory to avoid repeated free/malloc calls.  If\n * desired one can reclaim coder memory that is unused.\n */\nextern void\nrlpCoderReclaim (BRRlpCoder coder);\n\nextern void\nrlpCoderSetFailed (BRRlpCoder coder);\n\nextern void\nrlpCoderClrFailed (BRRlpCoder coder);\n\nextern int\nrlpCoderHasFailed (BRRlpCoder coder);\n\n//\n// RLP Data\n//\ntypedef struct {\n    size_t bytesCount;\n    uint8_t *bytes;\n} BRRlpData;\n\nextern BRRlpData\nrlpDataCopy (BRRlpData data);\n\nextern void\nrlpDataRelease (BRRlpData data);\n\n} BRRlpData;\n\nextern BRRlpData\ncreateRlpDataEmpty (void);\n\nextern void\nrlpDataRelease (BRRlpData data);\n\n/**\n * Convet the bytes in `data` into an `item`.  If `data` represents a RLP list, then `item` will\n * represent a list (thus `data` is \'walked\' to identify subitems, including sublists).\n */\nextern BRRlpItem\nrlpDataGetItem (BRRlpCoder coder, BRRlpData data);\n\n/**\n * Return the RLP data associated with `item`.  You own this data and must call\n * rlpDataRelese().\n */\nextern BRRlpData\nrlpItemGetData (BRRlpCoder coder, BRRlpItem item);\n\n/**\n * Return the RLP data associated with `item`.  You DO NOT own this data; you must not\n * modify the data nor release the data nor hold the data.  [The returned data is a direct\n * pointer into the `coder` memory and will become invalid on coder release.\n */\nextern BRRlpData\nrlpItemGetDataSharedDontRelease (BRRlpCoder coder, BRRlpItem item);\n\n/**\n * Extract the `bytes` and `bytesCount` for `item`.  The returns `bytes` will be the complete\n * RLP encoding for `item` which includes the RLP encoding of length.  Contrast this with\n * rlpDecodeItemBytes() which only returns the RLP encoding data w/o the length.\n * Extract the `item` data as a copy, filling `bytes` and `bytesCount`.\n *\n * TODO: ?? Hold onto BRRlpItem \'forever\'... then try to use... will fail because \'coder\'\n * TODO: will not have \'context\' ??\n */\n//extern void\n//rlpDataExtract (BRRlpItem item, uint8_t **bytes, size_t *bytesCount);\n\n//\n// UInt64\n//\nextern BRRlpItem\nrlpEncodeUInt64(BRRlpCoder coder, uint64_t value, int zeroAsEmptyString);\n\nextern uint64_t\nrlpDecodeUInt64(BRRlpCoder coder, BRRlpItem item, int zeroAsEmptyString);\n\n//\n// UInt256\n//\nextern BRRlpItem\nrlpEncodeUInt256(BRRlpCoder coder, UInt256 value, int zeroAsEmptyString);\n\nextern UInt256\nrlpDecodeUInt256(BRRlpCoder coder, BRRlpItem item, int zeroAsEmptyString);\n\n//\n// Double\n//\nextern BRRlpItem\nrlpEncodeDouble(BRRlpCoder coder, double value);\n\nextern double\nrlpDecodeDouble(BRRlpCoder coder, BRRlpItem item);\n\n//\n// Bytes\n//\nextern BRRlpItem\nrlpEncodeBytes (BRRlpCoder coder, uint8_t *bytes, size_t bytesCount);\n\nextern BRRlpItem\nrlpEncodeBytesPurgeLeadingZeros (BRRlpCoder coder, uint8_t *bytes, size_t bytesCount);\n\n/**\n * Extract the `data` for `item` as the item\'s bytes w/o the RLP encoding of length.  Thus, if\n * you used rlpEncodeItemBytes() to encode `bytes` and `bytesCount`, then the result of\n * rlpDecodeItemBytes() will be `data` with exactly the same data as was encoded.\n */\nextern BRRlpData\nrlpDecodeBytes (BRRlpCoder coder, BRRlpItem item);\n\nextern BRRlpData\nrlpDecodeBytesSharedDontRelease (BRRlpCoder coder, BRRlpItem item);\n\nextern BRRlpData\nrlpDecodeListSharedDontRelease (BRRlpCoder coder, BRRlpItem item);\n\n//\n// String\n//\nextern BRRlpItem\nrlpEncodeString (BRRlpCoder coder, const char *string);\n\nextern char *\nrlpDecodeString (BRRlpCoder coder, BRRlpItem item);\n\nextern int\nrlpDecodeStringCheck (BRRlpCoder coder, BRRlpItem item);\n\n//\n// Hex String\n//\nextern BRRlpItem\nrlpEncodeHexString (BRRlpCoder coder, const char *string);\n\nextern char *\nrlpDecodeHexString (BRRlpCoder coder, BRRlpItem item, const char *prefix);\n\n//\n// List\n//\nextern BRRlpItem\nrlpEncodeList1 (BRRlpCoder coder, BRRlpItem item1);\n\nextern BRRlpItem\nrlpEncodeList2 (BRRlpCoder coder, BRRlpItem item1, BRRlpItem item2);\n\nextern BRRlpItem\nrlpEncodeList (BRRlpCoder coder, size_t count, ...);\n\nextern BRRlpItem\nrlpEncodeListItems (BRRlpCoder coder, BRRlpItem *items, size_t itemsCount);\n\nextern const BRRlpItem *\nrlpDecodeList (BRRlpCoder coder, BRRlpItem item, size_t *itemsCount);\n    \n//\n// Show\n//\nextern void\nrlpItemShow (BRRlpCoder coder, BRRlpItem item, const char *topic);\n\nextern void\nrlpDataShow (BRRlpData data, const char *topic);\n\n//\n// Decode RLPData directly to numbers\n//   (Used for logGetData -> UInt256\n//\nextern UInt256\nrlpDataDecodeUInt256 (BRRlpData data);\n\nextern uint64_t\nrlpDataDecodeUInt64 (BRRlpData data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif //BR_RLP_Coder_H\n', 'message': "Merge branch 'feature/CORE-149' into ethereum\n", 'author_name': 'Ed Gamble', 'lang': 'h'}