/Users/eugenesiegel/btc/bitcoin/src/pubkey.h
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Copyright (c) 2009-2010 Satoshi Nakamoto | 
| 2 |  | // Copyright (c) 2009-present The Bitcoin Core developers | 
| 3 |  | // Copyright (c) 2017 The Zcash developers | 
| 4 |  | // Distributed under the MIT software license, see the accompanying | 
| 5 |  | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | 
| 6 |  |  | 
| 7 |  | #ifndef BITCOIN_PUBKEY_H | 
| 8 |  | #define BITCOIN_PUBKEY_H | 
| 9 |  |  | 
| 10 |  | #include <hash.h> | 
| 11 |  | #include <serialize.h> | 
| 12 |  | #include <span.h> | 
| 13 |  | #include <uint256.h> | 
| 14 |  |  | 
| 15 |  | #include <cstring> | 
| 16 |  | #include <optional> | 
| 17 |  | #include <vector> | 
| 18 |  |  | 
| 19 |  | const unsigned int BIP32_EXTKEY_SIZE = 74; | 
| 20 |  | const unsigned int BIP32_EXTKEY_WITH_VERSION_SIZE = 78; | 
| 21 |  |  | 
| 22 |  | /** A reference to a CKey: the Hash160 of its serialized public key */ | 
| 23 |  | class CKeyID : public uint160 | 
| 24 |  | { | 
| 25 |  | public: | 
| 26 | 324k |     CKeyID() : uint160() {} | 
| 27 | 0 |     explicit CKeyID(const uint160& in) : uint160(in) {} | 
| 28 |  | }; | 
| 29 |  |  | 
| 30 |  | typedef uint256 ChainCode; | 
| 31 |  |  | 
| 32 |  | /** An encapsulated public key. */ | 
| 33 |  | class CPubKey | 
| 34 |  | { | 
| 35 |  | public: | 
| 36 |  |     /** | 
| 37 |  |      * secp256k1: | 
| 38 |  |      */ | 
| 39 |  |     static constexpr unsigned int SIZE                   = 65; | 
| 40 |  |     static constexpr unsigned int COMPRESSED_SIZE        = 33; | 
| 41 |  |     static constexpr unsigned int SIGNATURE_SIZE         = 72; | 
| 42 |  |     static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65; | 
| 43 |  |     /** | 
| 44 |  |      * see www.keylength.com | 
| 45 |  |      * script supports up to 75 for single byte push | 
| 46 |  |      */ | 
| 47 |  |     static_assert( | 
| 48 |  |         SIZE >= COMPRESSED_SIZE, | 
| 49 |  |         "COMPRESSED_SIZE is larger than SIZE"); | 
| 50 |  |  | 
| 51 |  | private: | 
| 52 |  |  | 
| 53 |  |     /** | 
| 54 |  |      * Just store the serialized data. | 
| 55 |  |      * Its length can very cheaply be computed from the first byte. | 
| 56 |  |      */ | 
| 57 |  |     unsigned char vch[SIZE]; | 
| 58 |  |  | 
| 59 |  |     //! Compute the length of a pubkey with a given first byte. | 
| 60 |  |     unsigned int static GetLen(unsigned char chHeader) | 
| 61 | 0 |     { | 
| 62 | 0 |         if (chHeader == 2 || chHeader == 3) | 
| 63 | 0 |             return COMPRESSED_SIZE; | 
| 64 | 0 |         if (chHeader == 4 || chHeader == 6 || chHeader == 7) | 
| 65 | 0 |             return SIZE; | 
| 66 | 0 |         return 0; | 
| 67 | 0 |     } | 
| 68 |  |  | 
| 69 |  |     //! Set this key data to be invalid | 
| 70 |  |     void Invalidate() | 
| 71 | 324k |     { | 
| 72 | 324k |         vch[0] = 0xFF; | 
| 73 | 324k |     } | 
| 74 |  |  | 
| 75 |  | public: | 
| 76 |  |  | 
| 77 | 0 |     bool static ValidSize(const std::vector<unsigned char> &vch) { | 
| 78 | 0 |       return vch.size() > 0 && GetLen(vch[0]) == vch.size(); | 
| 79 | 0 |     } | 
| 80 |  |  | 
| 81 |  |     //! Construct an invalid public key. | 
| 82 |  |     CPubKey() | 
| 83 | 324k |     { | 
| 84 | 324k |         Invalidate(); | 
| 85 | 324k |     } | 
| 86 |  |  | 
| 87 |  |     //! Initialize a public key using begin/end iterators to byte data. | 
| 88 |  |     template <typename T> | 
| 89 |  |     void Set(const T pbegin, const T pend) | 
| 90 | 0 |     { | 
| 91 | 0 |         int len = pend == pbegin ? 0 : GetLen(pbegin[0]); | 
| 92 | 0 |         if (len && len == (pend - pbegin)) | 
| 93 | 0 |             memcpy(vch, (unsigned char*)&pbegin[0], len); | 
| 94 | 0 |         else | 
| 95 | 0 |             Invalidate(); | 
| 96 | 0 |     } Unexecuted instantiation: _ZN7CPubKey3SetINSt3__111__wrap_iterIPKhEEEEvT_S6_Unexecuted instantiation: _ZN7CPubKey3SetINSt3__111__wrap_iterIPhEEEEvT_S5_Unexecuted instantiation: _ZN7CPubKey3SetIPhEEvT_S2_Unexecuted instantiation: _ZN7CPubKey3SetIPKhEEvT_S3_ | 
| 97 |  |  | 
| 98 |  |     //! Construct a public key using begin/end iterators to byte data. | 
| 99 |  |     template <typename T> | 
| 100 |  |     CPubKey(const T pbegin, const T pend) | 
| 101 | 0 |     { | 
| 102 | 0 |         Set(pbegin, pend); | 
| 103 | 0 |     } Unexecuted instantiation: _ZN7CPubKeyC2INSt3__111__wrap_iterIPhEEEET_S5_Unexecuted instantiation: _ZN7CPubKeyC2INSt3__111__wrap_iterIPKhEEEET_S6_Unexecuted instantiation: _ZN7CPubKeyC2IPhEET_S2_ | 
| 104 |  |  | 
| 105 |  |     //! Construct a public key from a byte vector. | 
| 106 |  |     explicit CPubKey(std::span<const uint8_t> _vch) | 
| 107 | 0 |     { | 
| 108 | 0 |         Set(_vch.begin(), _vch.end()); | 
| 109 | 0 |     } | 
| 110 |  |  | 
| 111 |  |     //! Simple read-only vector-like interface to the pubkey data. | 
| 112 | 0 |     unsigned int size() const { return GetLen(vch[0]); } | 
| 113 | 0 |     const unsigned char* data() const { return vch; } | 
| 114 | 0 |     const unsigned char* begin() const { return vch; } | 
| 115 | 0 |     const unsigned char* end() const { return vch + size(); } | 
| 116 | 0 |     const unsigned char& operator[](unsigned int pos) const { return vch[pos]; } | 
| 117 |  |  | 
| 118 |  |     //! Comparator implementation. | 
| 119 |  |     friend bool operator==(const CPubKey& a, const CPubKey& b) | 
| 120 | 0 |     { | 
| 121 | 0 |         return a.vch[0] == b.vch[0] && | 
| 122 | 0 |                memcmp(a.vch, b.vch, a.size()) == 0; | 
| 123 | 0 |     } | 
| 124 |  |     friend bool operator!=(const CPubKey& a, const CPubKey& b) | 
| 125 | 0 |     { | 
| 126 | 0 |         return !(a == b); | 
| 127 | 0 |     } | 
| 128 |  |     friend bool operator<(const CPubKey& a, const CPubKey& b) | 
| 129 | 0 |     { | 
| 130 | 0 |         return a.vch[0] < b.vch[0] || | 
| 131 | 0 |                (a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0); | 
| 132 | 0 |     } | 
| 133 |  |     friend bool operator>(const CPubKey& a, const CPubKey& b) | 
| 134 | 0 |     { | 
| 135 | 0 |         return a.vch[0] > b.vch[0] || | 
| 136 | 0 |                (a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) > 0); | 
| 137 | 0 |     } | 
| 138 |  |  | 
| 139 |  |     //! Implement serialization, as if this was a byte vector. | 
| 140 |  |     template <typename Stream> | 
| 141 |  |     void Serialize(Stream& s) const | 
| 142 | 0 |     { | 
| 143 | 0 |         unsigned int len = size(); | 
| 144 | 0 |         ::WriteCompactSize(s, len); | 
| 145 | 0 |         s << std::span{vch, len}; | 
| 146 | 0 |     } | 
| 147 |  |     template <typename Stream> | 
| 148 |  |     void Unserialize(Stream& s) | 
| 149 | 0 |     { | 
| 150 | 0 |         const unsigned int len(::ReadCompactSize(s)); | 
| 151 | 0 |         if (len <= SIZE) { | 
| 152 | 0 |             s >> std::span{vch, len}; | 
| 153 | 0 |             if (len != size()) { | 
| 154 | 0 |                 Invalidate(); | 
| 155 | 0 |             } | 
| 156 | 0 |         } else { | 
| 157 |  |             // invalid pubkey, skip available data | 
| 158 | 0 |             s.ignore(len); | 
| 159 | 0 |             Invalidate(); | 
| 160 | 0 |         } | 
| 161 | 0 |     } | 
| 162 |  |  | 
| 163 |  |     //! Get the KeyID of this public key (hash of its serialization) | 
| 164 |  |     CKeyID GetID() const | 
| 165 | 0 |     { | 
| 166 | 0 |         return CKeyID(Hash160(std::span{vch}.first(size()))); | 
| 167 | 0 |     } | 
| 168 |  |  | 
| 169 |  |     //! Get the 256-bit hash of this public key. | 
| 170 |  |     uint256 GetHash() const | 
| 171 | 0 |     { | 
| 172 | 0 |         return Hash(std::span{vch}.first(size())); | 
| 173 | 0 |     } | 
| 174 |  |  | 
| 175 |  |     /* | 
| 176 |  |      * Check syntactic correctness. | 
| 177 |  |      * | 
| 178 |  |      * When setting a pubkey (Set()) or deserializing fails (its header bytes | 
| 179 |  |      * don't match the length of the data), the size is set to 0. Thus, | 
| 180 |  |      * by checking size, one can observe whether Set() or deserialization has | 
| 181 |  |      * failed. | 
| 182 |  |      * | 
| 183 |  |      * This does not check for more than that. In particular, it does not verify | 
| 184 |  |      * that the coordinates correspond to a point on the curve (see IsFullyValid() | 
| 185 |  |      * for that instead). | 
| 186 |  |      * | 
| 187 |  |      * Note that this is consensus critical as CheckECDSASignature() calls it! | 
| 188 |  |      */ | 
| 189 |  |     bool IsValid() const | 
| 190 | 0 |     { | 
| 191 | 0 |         return size() > 0; | 
| 192 | 0 |     } | 
| 193 |  |  | 
| 194 |  |     /** Check if a public key is a syntactically valid compressed or uncompressed key. */ | 
| 195 |  |     bool IsValidNonHybrid() const noexcept | 
| 196 | 0 |     { | 
| 197 | 0 |         return size() > 0 && (vch[0] == 0x02 || vch[0] == 0x03 || vch[0] == 0x04); | 
| 198 | 0 |     } | 
| 199 |  |  | 
| 200 |  |     //! fully validate whether this is a valid public key (more expensive than IsValid()) | 
| 201 |  |     bool IsFullyValid() const; | 
| 202 |  |  | 
| 203 |  |     //! Check whether this is a compressed public key. | 
| 204 |  |     bool IsCompressed() const | 
| 205 | 0 |     { | 
| 206 | 0 |         return size() == COMPRESSED_SIZE; | 
| 207 | 0 |     } | 
| 208 |  |  | 
| 209 |  |     /** | 
| 210 |  |      * Verify a DER signature (~72 bytes). | 
| 211 |  |      * If this public key is not fully valid, the return value will be false. | 
| 212 |  |      */ | 
| 213 |  |     bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const; | 
| 214 |  |  | 
| 215 |  |     /** | 
| 216 |  |      * Check whether a signature is normalized (lower-S). | 
| 217 |  |      */ | 
| 218 |  |     static bool CheckLowS(const std::vector<unsigned char>& vchSig); | 
| 219 |  |  | 
| 220 |  |     //! Recover a public key from a compact signature. | 
| 221 |  |     bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig); | 
| 222 |  |  | 
| 223 |  |     //! Turn this public key into an uncompressed public key. | 
| 224 |  |     bool Decompress(); | 
| 225 |  |  | 
| 226 |  |     //! Derive BIP32 child pubkey. | 
| 227 |  |     [[nodiscard]] bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const; | 
| 228 |  | }; | 
| 229 |  |  | 
| 230 |  | class XOnlyPubKey | 
| 231 |  | { | 
| 232 |  | private: | 
| 233 |  |     uint256 m_keydata; | 
| 234 |  |  | 
| 235 |  | public: | 
| 236 |  |     /** Nothing Up My Sleeve point H | 
| 237 |  |      *  Used as an internal key for provably disabling the key path spend | 
| 238 |  |      *  see BIP341 for more details */ | 
| 239 |  |     static const XOnlyPubKey NUMS_H; | 
| 240 |  |  | 
| 241 |  |     /** Construct an empty x-only pubkey. */ | 
| 242 | 0 |     XOnlyPubKey() = default; | 
| 243 |  |  | 
| 244 |  |     XOnlyPubKey(const XOnlyPubKey&) = default; | 
| 245 |  |     XOnlyPubKey& operator=(const XOnlyPubKey&) = default; | 
| 246 |  |  | 
| 247 |  |     /** Determine if this pubkey is fully valid. This is true for approximately 50% of all | 
| 248 |  |      *  possible 32-byte arrays. If false, VerifySchnorr, CheckTapTweak and CreateTapTweak | 
| 249 |  |      *  will always fail. */ | 
| 250 |  |     bool IsFullyValid() const; | 
| 251 |  |  | 
| 252 |  |     /** Test whether this is the 0 key (the result of default construction). This implies | 
| 253 |  |      *  !IsFullyValid(). */ | 
| 254 | 0 |     bool IsNull() const { return m_keydata.IsNull(); } | 
| 255 |  |  | 
| 256 |  |     /** Construct an x-only pubkey from exactly 32 bytes. */ | 
| 257 | 0 |     constexpr explicit XOnlyPubKey(std::span<const unsigned char> bytes) : m_keydata{bytes} {} | 
| 258 |  |  | 
| 259 |  |     /** Construct an x-only pubkey from a normal pubkey. */ | 
| 260 | 0 |     explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(std::span{pubkey}.subspan(1, 32)) {} | 
| 261 |  |  | 
| 262 |  |     /** Verify a Schnorr signature against this public key. | 
| 263 |  |      * | 
| 264 |  |      * sigbytes must be exactly 64 bytes. | 
| 265 |  |      */ | 
| 266 |  |     bool VerifySchnorr(const uint256& msg, std::span<const unsigned char> sigbytes) const; | 
| 267 |  |  | 
| 268 |  |     /** Compute the Taproot tweak as specified in BIP341, with *this as internal | 
| 269 |  |      * key: | 
| 270 |  |      *  - if merkle_root == nullptr: H_TapTweak(xonly_pubkey) | 
| 271 |  |      *  - otherwise:                 H_TapTweak(xonly_pubkey || *merkle_root) | 
| 272 |  |      * | 
| 273 |  |      * Note that the behavior of this function with merkle_root != nullptr is | 
| 274 |  |      * consensus critical. | 
| 275 |  |      */ | 
| 276 |  |     uint256 ComputeTapTweakHash(const uint256* merkle_root) const; | 
| 277 |  |  | 
| 278 |  |     /** Verify that this is a Taproot tweaked output point, against a specified internal key, | 
| 279 |  |      *  Merkle root, and parity. */ | 
| 280 |  |     bool CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const; | 
| 281 |  |  | 
| 282 |  |     /** Construct a Taproot tweaked output point with this point as internal key. */ | 
| 283 |  |     std::optional<std::pair<XOnlyPubKey, bool>> CreateTapTweak(const uint256* merkle_root) const; | 
| 284 |  |  | 
| 285 |  |     /** Returns a list of CKeyIDs for the CPubKeys that could have been used to create this XOnlyPubKey. | 
| 286 |  |      * As the CKeyID is the Hash160(full pubkey), the produced CKeyIDs are for the versions of this | 
| 287 |  |      * XOnlyPubKey with 0x02 and 0x03 prefixes. | 
| 288 |  |      * This is needed for key lookups since keys are indexed by CKeyID. | 
| 289 |  |      */ | 
| 290 |  |     std::vector<CKeyID> GetKeyIDs() const; | 
| 291 |  |     /** Returns this XOnlyPubKey with 0x02 and 0x03 prefixes */ | 
| 292 |  |     std::vector<CPubKey> GetCPubKeys() const; | 
| 293 |  |  | 
| 294 |  |     CPubKey GetEvenCorrespondingCPubKey() const; | 
| 295 |  |  | 
| 296 | 0 |     const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); } | 
| 297 | 0 |     static constexpr size_t size() { return decltype(m_keydata)::size(); } | 
| 298 | 0 |     const unsigned char* data() const { return m_keydata.begin(); } | 
| 299 | 0 |     const unsigned char* begin() const { return m_keydata.begin(); } | 
| 300 | 0 |     const unsigned char* end() const { return m_keydata.end(); } | 
| 301 | 0 |     unsigned char* data() { return m_keydata.begin(); } | 
| 302 | 0 |     unsigned char* begin() { return m_keydata.begin(); } | 
| 303 | 0 |     unsigned char* end() { return m_keydata.end(); } | 
| 304 | 0 |     bool operator==(const XOnlyPubKey& other) const { return m_keydata == other.m_keydata; } | 
| 305 | 0 |     bool operator!=(const XOnlyPubKey& other) const { return m_keydata != other.m_keydata; } | 
| 306 | 0 |     bool operator<(const XOnlyPubKey& other) const { return m_keydata < other.m_keydata; } | 
| 307 |  |  | 
| 308 |  |     //! Implement serialization without length prefixes since it is a fixed length | 
| 309 | 0 |     SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); }| Line | Count | Source |  | 145 | 0 | #define READWRITE(...) (ser_action.SerReadWriteMany(s, __VA_ARGS__)) | 
 |     SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); }| Line | Count | Source |  | 145 | 0 | #define READWRITE(...) (ser_action.SerReadWriteMany(s, __VA_ARGS__)) | 
 |     SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); }| Line | Count | Source |  | 145 | 0 | #define READWRITE(...) (ser_action.SerReadWriteMany(s, __VA_ARGS__)) | 
 |     SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); }| Line | Count | Source |  | 145 | 0 | #define READWRITE(...) (ser_action.SerReadWriteMany(s, __VA_ARGS__)) | 
Unexecuted instantiation: _ZN11XOnlyPubKey16SerializationOpsI10SpanReaderS_17ActionUnserializeEEvRT0_RT_T1_Unexecuted instantiation: _ZN11XOnlyPubKey16SerializationOpsI10DataStreamS_17ActionUnserializeEEvRT0_RT_T1_Unexecuted instantiation: _ZN11XOnlyPubKey16SerializationOpsI12SizeComputerKS_15ActionSerializeEEvRT0_RT_T1_Unexecuted instantiation: _ZN11XOnlyPubKey16SerializationOpsI10DataStreamKS_15ActionSerializeEEvRT0_RT_T1_ | 
| 310 |  | }; | 
| 311 |  |  | 
| 312 |  | /** An ElligatorSwift-encoded public key. */ | 
| 313 |  | struct EllSwiftPubKey | 
| 314 |  | { | 
| 315 |  | private: | 
| 316 |  |     static constexpr size_t SIZE = 64; | 
| 317 |  |     std::array<std::byte, SIZE> m_pubkey; | 
| 318 |  |  | 
| 319 |  | public: | 
| 320 |  |     /** Default constructor creates all-zero pubkey (which is valid). */ | 
| 321 |  |     EllSwiftPubKey() noexcept = default; | 
| 322 |  |  | 
| 323 |  |     /** Construct a new ellswift public key from a given serialization. */ | 
| 324 |  |     EllSwiftPubKey(std::span<const std::byte> ellswift) noexcept; | 
| 325 |  |  | 
| 326 |  |     /** Decode to normal compressed CPubKey (for debugging purposes). */ | 
| 327 |  |     CPubKey Decode() const; | 
| 328 |  |  | 
| 329 |  |     // Read-only access for serialization. | 
| 330 | 0 |     const std::byte* data() const { return m_pubkey.data(); } | 
| 331 | 0 |     static constexpr size_t size() { return SIZE; } | 
| 332 | 0 |     auto begin() const { return m_pubkey.cbegin(); } | 
| 333 | 0 |     auto end() const { return m_pubkey.cend(); } | 
| 334 |  |  | 
| 335 |  |     bool friend operator==(const EllSwiftPubKey& a, const EllSwiftPubKey& b) | 
| 336 | 0 |     { | 
| 337 | 0 |         return a.m_pubkey == b.m_pubkey; | 
| 338 | 0 |     } | 
| 339 |  |  | 
| 340 |  |     bool friend operator!=(const EllSwiftPubKey& a, const EllSwiftPubKey& b) | 
| 341 | 0 |     { | 
| 342 | 0 |         return a.m_pubkey != b.m_pubkey; | 
| 343 | 0 |     } | 
| 344 |  | }; | 
| 345 |  |  | 
| 346 |  | struct CExtPubKey { | 
| 347 |  |     unsigned char version[4]; | 
| 348 |  |     unsigned char nDepth; | 
| 349 |  |     unsigned char vchFingerprint[4]; | 
| 350 |  |     unsigned int nChild; | 
| 351 |  |     ChainCode chaincode; | 
| 352 |  |     CPubKey pubkey; | 
| 353 |  |  | 
| 354 |  |     friend bool operator==(const CExtPubKey &a, const CExtPubKey &b) | 
| 355 | 0 |     { | 
| 356 | 0 |         return a.nDepth == b.nDepth && | 
| 357 | 0 |             memcmp(a.vchFingerprint, b.vchFingerprint, sizeof(vchFingerprint)) == 0 && | 
| 358 | 0 |             a.nChild == b.nChild && | 
| 359 | 0 |             a.chaincode == b.chaincode && | 
| 360 | 0 |             a.pubkey == b.pubkey; | 
| 361 | 0 |     } | 
| 362 |  |  | 
| 363 |  |     friend bool operator!=(const CExtPubKey &a, const CExtPubKey &b) | 
| 364 | 0 |     { | 
| 365 | 0 |         return !(a == b); | 
| 366 | 0 |     } | 
| 367 |  |  | 
| 368 |  |     friend bool operator<(const CExtPubKey &a, const CExtPubKey &b) | 
| 369 | 0 |     { | 
| 370 | 0 |         if (a.pubkey < b.pubkey) { | 
| 371 | 0 |             return true; | 
| 372 | 0 |         } else if (a.pubkey > b.pubkey) { | 
| 373 | 0 |             return false; | 
| 374 | 0 |         } | 
| 375 | 0 |         return a.chaincode < b.chaincode; | 
| 376 | 0 |     } | 
| 377 |  |  | 
| 378 |  |     void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const; | 
| 379 |  |     void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]); | 
| 380 |  |     void EncodeWithVersion(unsigned char code[BIP32_EXTKEY_WITH_VERSION_SIZE]) const; | 
| 381 |  |     void DecodeWithVersion(const unsigned char code[BIP32_EXTKEY_WITH_VERSION_SIZE]); | 
| 382 |  |     [[nodiscard]] bool Derive(CExtPubKey& out, unsigned int nChild) const; | 
| 383 |  | }; | 
| 384 |  |  | 
| 385 |  | #endif // BITCOIN_PUBKEY_H |