| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
12 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,7 @@ AC_PREREQ([2.69]) | |||
| 2 | 2 | define(_CLIENT_VERSION_MAJOR, 23) | |
| 3 | 3 | define(_CLIENT_VERSION_MINOR, 3) | |
| 4 | 4 | define(_CLIENT_VERSION_BUILD, 4) | |
| 5 | - define(_CLIENT_VERSION_RC, 1) | ||
| 5 | + define(_CLIENT_VERSION_RC, 0) | ||
| 6 | 6 | define(_CLIENT_VERSION_IS_RELEASE, true) | |
| 7 | 7 | define(_COPYRIGHT_YEAR, 2026) | |
| 8 | 8 | define(_COPYRIGHT_HOLDERS,[The %s developers]) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. | |
| 2 | - .TH ELEMENTS-CLI "1" "June 2026" "elements-cli v23.3.4" "User Commands" | ||
| 2 | + .TH ELEMENTS-CLI "1" "September 2026" "elements-cli v23.3.4" "User Commands" | ||
| 3 | 3 | .SH NAME | |
| 4 | 4 | elements-cli \- manual page for elements-cli v23.3.4 | |
| 5 | 5 | .SH SYNOPSIS | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. | |
| 2 | - .TH ELEMENTS-QT "1" "June 2026" "elements-qt v23.3.4" "User Commands" | ||
| 2 | + .TH ELEMENTS-QT "1" "September 2026" "elements-qt v23.3.4" "User Commands" | ||
| 3 | 3 | .SH NAME | |
| 4 | 4 | elements-qt \- manual page for elements-qt v23.3.4 | |
| 5 | 5 | .SH SYNOPSIS | |
@@ -115,7 +115,7 @@ Do not keep transactions in the mempool longer than <n> hours (default: | |||
| 115 | 115 | .HP | |
| 116 | 116 | \fB\-par=\fR<n> | |
| 117 | 117 | .IP | |
| 118 | - Set the number of script verification threads (\fB\-16\fR to 15, 0 = auto, <0 = | ||
| 118 | + Set the number of script verification threads (\fB\-24\fR to 15, 0 = auto, <0 = | ||
| 119 | 119 | leave that many cores free, default: 0) | |
| 120 | 120 | .HP | |
| 121 | 121 | \fB\-persistmempool\fR | |
@@ -318,10 +318,6 @@ created within past week. 0 = no limit (default: 0M). Optional | |||
| 318 | 318 | suffix units [k|K|m|M|g|G|t|T] (default: M). Lowercase is 1000 | |
| 319 | 319 | base while uppercase is 1024 base | |
| 320 | 320 | .HP | |
| 321 | - \fB\-natpmp\fR | ||
| 322 | - .IP | ||
| 323 | - Use NAT\-PMP to map the listening port (default: 0) | ||
| 324 | - .HP | ||
| 325 | 321 | \fB\-networkactive\fR | |
| 326 | 322 | .IP | |
| 327 | 323 | Enable all P2P network activity (default: 1). Can be changed by the | |
@@ -390,10 +386,6 @@ Tor control port to use if onion listening enabled (default: | |||
| 390 | 386 | .IP | |
| 391 | 387 | Tor control port password (default: empty) | |
| 392 | 388 | .HP | |
| 393 | - \fB\-upnp\fR | ||
| 394 | - .IP | ||
| 395 | - Use UPnP to map the listening port (default: 0) | ||
| 396 | - .HP | ||
| 397 | 389 | \fB\-whitebind=\fR<[permissions@]addr> | |
| 398 | 390 | .IP | |
| 399 | 391 | Bind to the given address and add permission flags to the peers | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. | |
| 2 | - .TH ELEMENTS-TX "1" "June 2026" "elements-tx v23.3.4" "User Commands" | ||
| 2 | + .TH ELEMENTS-TX "1" "September 2026" "elements-tx v23.3.4" "User Commands" | ||
| 3 | 3 | .SH NAME | |
| 4 | 4 | elements-tx \- manual page for elements-tx v23.3.4 | |
| 5 | 5 | .SH SYNOPSIS | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. | |
| 2 | - .TH ELEMENTS-UTIL "1" "June 2026" "elements-util v23.3.4" "User Commands" | ||
| 2 | + .TH ELEMENTS-UTIL "1" "September 2026" "elements-util v23.3.4" "User Commands" | ||
| 3 | 3 | .SH NAME | |
| 4 | 4 | elements-util \- manual page for elements-util v23.3.4 | |
| 5 | 5 | .SH SYNOPSIS | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. | |
| 2 | - .TH ELEMENTS-WALLET "1" "June 2026" "elements-wallet v23.3.4" "User Commands" | ||
| 2 | + .TH ELEMENTS-WALLET "1" "September 2026" "elements-wallet v23.3.4" "User Commands" | ||
| 3 | 3 | .SH NAME | |
| 4 | 4 | elements-wallet \- manual page for elements-wallet v23.3.4 | |
| 5 | 5 | .SH DESCRIPTION | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. | |
| 2 | - .TH ELEMENTSD "1" "June 2026" "elementsd v23.3.4" "User Commands" | ||
| 2 | + .TH ELEMENTSD "1" "September 2026" "elementsd v23.3.4" "User Commands" | ||
| 3 | 3 | .SH NAME | |
| 4 | 4 | elementsd \- manual page for elementsd v23.3.4 | |
| 5 | 5 | .SH SYNOPSIS | |
@@ -115,7 +115,7 @@ Do not keep transactions in the mempool longer than <n> hours (default: | |||
| 115 | 115 | .HP | |
| 116 | 116 | \fB\-par=\fR<n> | |
| 117 | 117 | .IP | |
| 118 | - Set the number of script verification threads (\fB\-16\fR to 15, 0 = auto, <0 = | ||
| 118 | + Set the number of script verification threads (\fB\-24\fR to 15, 0 = auto, <0 = | ||
| 119 | 119 | leave that many cores free, default: 0) | |
| 120 | 120 | .HP | |
| 121 | 121 | \fB\-persistmempool\fR | |
@@ -318,10 +318,6 @@ created within past week. 0 = no limit (default: 0M). Optional | |||
| 318 | 318 | suffix units [k|K|m|M|g|G|t|T] (default: M). Lowercase is 1000 | |
| 319 | 319 | base while uppercase is 1024 base | |
| 320 | 320 | .HP | |
| 321 | - \fB\-natpmp\fR | ||
| 322 | - .IP | ||
| 323 | - Use NAT\-PMP to map the listening port (default: 0) | ||
| 324 | - .HP | ||
| 325 | 321 | \fB\-networkactive\fR | |
| 326 | 322 | .IP | |
| 327 | 323 | Enable all P2P network activity (default: 1). Can be changed by the | |
@@ -390,10 +386,6 @@ Tor control port to use if onion listening enabled (default: | |||
| 390 | 386 | .IP | |
| 391 | 387 | Tor control port password (default: empty) | |
| 392 | 388 | .HP | |
| 393 | - \fB\-upnp\fR | ||
| 394 | - .IP | ||
| 395 | - Use UPnP to map the listening port (default: 0) | ||
| 396 | - .HP | ||
| 397 | 389 | \fB\-whitebind=\fR<[permissions@]addr> | |
| 398 | 390 | .IP | |
| 399 | 391 | Bind to the given address and add permission flags to the peers | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -132,6 +132,7 @@ BITCOIN_TESTS =\ | |||
| 132 | 132 | test/serialize_tests.cpp \ | |
| 133 | 133 | test/settings_tests.cpp \ | |
| 134 | 134 | test/sighash_tests.cpp \ | |
| 135 | + test/sigcache_tests.cpp \ | ||
| 135 | 136 | test/sigopcount_tests.cpp \ | |
| 136 | 137 | test/skiplist_tests.cpp \ | |
| 137 | 138 | test/sock_tests.cpp \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -556,6 +556,7 @@ void SetupServerArgs(ArgsManager& argsman) | |||
| 556 | 556 | argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); | |
| 557 | 557 | argsman.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); | |
| 558 | 558 | argsman.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); | |
| 559 | + argsman.AddArg("-rangeproofcache", strprintf("Enable the range proof validation cache (default: %u). Use -norangeproofcache to disable.", 1), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); | ||
| 559 | 560 | argsman.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); | |
| 560 | 561 | argsman.AddArg("-printpriority", strprintf("Log transaction fee rate in " + CURRENCY_UNIT + "/kvB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); | |
| 561 | 562 | argsman.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,6 +9,7 @@ | |||
| 9 | 9 | #include <random.h> | |
| 10 | 10 | #include <uint256.h> | |
| 11 | 11 | #include <util/system.h> | |
| 12 | + #include <hash.h> | ||
| 12 | 13 | ||
| 13 | 14 | #include <cuckoocache.h> | |
| 14 | 15 | ||
@@ -26,23 +27,23 @@ namespace { | |||
| 26 | 27 | class CSignatureCache | |
| 27 | 28 | { | |
| 28 | 29 | private: | |
| 29 | - //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature): | ||
| 30 | + //! Salted SHA256 midstates, domain-separated by signature or proof type. | ||
| 30 | 31 | CSHA256 m_salted_hasher_ecdsa; | |
| 31 | 32 | CSHA256 m_salted_hasher_schnorr; | |
| 32 | - CSHA256 m_salted_hasher_range_proof; | ||
| 33 | - CSHA256 m_salted_hasher_surjection_proof; | ||
| 33 | + CHashWriter m_salted_hasher_range_proof; | ||
| 34 | + CHashWriter m_salted_hasher_surjection_proof; | ||
| 34 | 35 | typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type; | |
| 35 | 36 | map_type setValid; | |
| 36 | 37 | std::shared_mutex cs_sigcache; | |
| 37 | 38 | ||
| 38 | 39 | public: | |
| 39 | - CSignatureCache() | ||
| 40 | + CSignatureCache(): | ||
| 41 | + m_salted_hasher_range_proof(SER_GETHASH,0), | ||
| 42 | + m_salted_hasher_surjection_proof(SER_GETHASH,0) | ||
| 40 | 43 | { | |
| 41 | 44 | uint256 nonce = GetRandHash(); | |
| 42 | - // We want the nonce to be 64 bytes long to force the hasher to process | ||
| 43 | - // this chunk, which makes later hash computations more efficient. We | ||
| 44 | - // just write our 32-byte entropy, and then pad with 'E' for ECDSA and | ||
| 45 | - // 'S' for Schnorr (followed by 0 bytes). | ||
| 45 | + // Use 64-byte, type-specific salted midstates so later hash computations | ||
| 46 | + // can start after the first SHA256 chunk. | ||
| 46 | 47 | static constexpr unsigned char PADDING_ECDSA[32] = {'E'}; | |
| 47 | 48 | static constexpr unsigned char PADDING_SCHNORR[32] = {'S'}; | |
| 48 | 49 | static constexpr unsigned char PADDING_RANGE_PROOF[32] = {'r'}; | |
@@ -51,10 +52,8 @@ class CSignatureCache | |||
| 51 | 52 | m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32); | |
| 52 | 53 | m_salted_hasher_schnorr.Write(nonce.begin(), 32); | |
| 53 | 54 | m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32); | |
| 54 | - m_salted_hasher_range_proof.Write(nonce.begin(), 32); | ||
| 55 | - m_salted_hasher_range_proof.Write(PADDING_RANGE_PROOF, 32); | ||
| 56 | - m_salted_hasher_surjection_proof.Write(nonce.begin(), 32); | ||
| 57 | - m_salted_hasher_surjection_proof.Write(PADDING_SURJECTION_PROOF, 32); | ||
| 55 | + m_salted_hasher_range_proof << nonce << PADDING_RANGE_PROOF; | ||
| 56 | + m_salted_hasher_surjection_proof << nonce << PADDING_SURJECTION_PROOF; | ||
| 58 | 57 | } | |
| 59 | 58 | ||
| 60 | 59 | void | |
@@ -72,13 +71,39 @@ class CSignatureCache | |||
| 72 | 71 | } | |
| 73 | 72 | ||
| 74 | 73 | // ELEMENTS: | |
| 75 | - void ComputeEntryRangeProof(uint256& entry, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment, const std::vector<unsigned char>& asset_commitment, const CScript& scriptPubKey) { | ||
| 76 | - CSHA256 hasher = m_salted_hasher_range_proof; | ||
| 77 | - hasher.Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Write(asset_commitment.data(), asset_commitment.size()).Write(scriptPubKey.data(), scriptPubKey.size()).Finalize(entry.begin()); | ||
| 74 | + void ComputeEntryRangeProof(uint256& entry, | ||
| 75 | + const std::vector<unsigned char>& proof, | ||
| 76 | + const std::vector<unsigned char>& commitment, | ||
| 77 | + const std::vector<unsigned char>& asset_commitment, | ||
| 78 | + const CScript& script_pub_key) const | ||
| 79 | + { | ||
| 80 | + CHashWriter hasher = m_salted_hasher_range_proof; | ||
| 81 | + // We commit to both commitments and the scriptPubKey because these are | ||
| 82 | + // committed to by the rangeproof itself; a change in any of them would | ||
| 83 | + // invalidate the proof. Since these are exactly the arguments to | ||
| 84 | + // CachingRangeProofChecker::VerifyRangeProof (below), there is no | ||
| 85 | + // additional data that could affect the rangeproof's validity. | ||
| 86 | + // Serialization length-prefixes every field, including the variable-length | ||
| 87 | + // proof and script, so distinct argument tuples cannot share an encoding. | ||
| 88 | + hasher << proof << commitment << asset_commitment << script_pub_key; | ||
| 89 | + entry = hasher.GetSHA256(); | ||
| 78 | 90 | } | |
| 79 | - void ComputeEntrySurjectionProof(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) { | ||
| 80 | - CSHA256 hasher = m_salted_hasher_surjection_proof; | ||
| 81 | - hasher.Write(hash.begin(), 32).Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin()); | ||
| 91 | + void ComputeEntrySurjectionProof(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment, const std::vector<secp256k1_generator>& vTags) const { | ||
| 92 | + CHashWriter hasher = m_salted_hasher_surjection_proof; | ||
| 93 | + // We hash all arguments passed to CachingSurjectionProofChecker::VerifySurjectionProof, | ||
| 94 | + // to ensure that any change in the way that the verification function is called will | ||
| 95 | + // trigger a cache miss and explicit verification. However, we note that the `wtxid` | ||
| 96 | + // (hash) commits to all the other data such that we could technically hash only it. | ||
| 97 | + // We retain the other data as a defense against future refactorings. | ||
| 98 | + // | ||
| 99 | + // Serialize vTags as a flat byte vector (each secp256k1_generator is 64 bytes). | ||
| 100 | + std::vector<unsigned char> vTagsBytes; | ||
| 101 | + vTagsBytes.reserve(vTags.size() * 64); | ||
| 102 | + for (const auto& tag : vTags) { | ||
| 103 | + vTagsBytes.insert(vTagsBytes.end(), std::begin(tag.data), std::end(tag.data)); | ||
| 104 | + } | ||
| 105 | + hasher << hash << proof << commitment << vTagsBytes; | ||
| 106 | + entry = hasher.GetSHA256(); | ||
| 82 | 107 | } | |
| 83 | 108 | ||
| 84 | 109 | bool | |
@@ -154,6 +179,10 @@ bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsig | |||
| 154 | 179 | // To be called once in AppInit2/TestingSetup to initialize the rangeproof cache | |
| 155 | 180 | void InitRangeproofCache() | |
| 156 | 181 | { | |
| 182 | + if (!gArgs.GetBoolArg("-rangeproofcache", true)) { | ||
| 183 | + LogPrintf("Range proof cache disabled via -norangeproofcache\n"); | ||
| 184 | + return; | ||
| 185 | + } | ||
| 157 | 186 | // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero, | |
| 158 | 187 | // setup_bytes creates the minimum possible cache (2 elements). | |
| 159 | 188 | size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 4), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); | |
@@ -175,11 +204,18 @@ void InitSurjectionproofCache() | |||
| 175 | 204 | ||
| 176 | 205 | bool CachingRangeProofChecker::VerifyRangeProof(const std::vector<unsigned char>& vchRangeProof, const std::vector<unsigned char>& vchValueCommitment, const std::vector<unsigned char>& vchAssetCommitment, const CScript& scriptPubKey, const secp256k1_context* secp256k1_ctx_verify_amounts) const | |
| 177 | 206 | { | |
| 207 | + // ELEMENTS: NOTE FOR FUTURE EDITORS: every argument to this function that | ||
| 208 | + // carries data (i.e. everything except the secp256k1 context, which is | ||
| 209 | + // stateless) MUST be included in ComputeEntryRangeProof. Omitting any | ||
| 210 | + // argument risks returning a cached positive result for a proof that was | ||
| 211 | + // verified with different inputs. | ||
| 178 | 212 | uint256 entry; | |
| 179 | - rangeProofCache.ComputeEntryRangeProof(entry, vchRangeProof, vchValueCommitment, vchAssetCommitment, scriptPubKey); | ||
| 180 | - | ||
| 181 | - if (rangeProofCache.Get(entry, !store)) { | ||
| 182 | - return true; | ||
| 213 | + const bool useCache = gArgs.GetBoolArg("-rangeproofcache", true); | ||
| 214 | + if (useCache) { | ||
| 215 | + rangeProofCache.ComputeEntryRangeProof(entry, vchRangeProof, vchValueCommitment, vchAssetCommitment, scriptPubKey); | ||
| 216 | + if (rangeProofCache.Get(entry, !store)) { | ||
| 217 | + return true; | ||
| 218 | + } | ||
| 183 | 219 | } | |
| 184 | 220 | ||
| 185 | 221 | if (vchRangeProof.size() == 0) { | |
@@ -208,7 +244,7 @@ bool CachingRangeProofChecker::VerifyRangeProof(const std::vector<unsigned char> | |||
| 208 | 244 | return false; | |
| 209 | 245 | } | |
| 210 | 246 | ||
| 211 | - if (store) { | ||
| 247 | + if (useCache && store) { | ||
| 212 | 248 | rangeProofCache.Set(entry); | |
| 213 | 249 | } | |
| 214 | 250 | ||
@@ -227,7 +263,7 @@ bool CachingSurjectionProofChecker::VerifySurjectionProof(secp256k1_surjectionpr | |||
| 227 | 263 | // wtxid commits to all data including surj targets | |
| 228 | 264 | // we need to specify the proof and output asset point to be unique | |
| 229 | 265 | uint256 entry; | |
| 230 | - surjectionProofCache.ComputeEntrySurjectionProof(entry, wtxid, vchproof, std::vector<unsigned char>(std::begin(gen.data), std::end(gen.data))); | ||
| 266 | + surjectionProofCache.ComputeEntrySurjectionProof(entry, wtxid, vchproof, std::vector<unsigned char>(std::begin(gen.data), std::end(gen.data)), vTags); | ||
| 231 | 267 | ||
| 232 | 268 | if (surjectionProofCache.Get(entry, !store)) { | |
| 233 | 269 | return true; | |
@@ -244,5 +280,24 @@ bool CachingSurjectionProofChecker::VerifySurjectionProof(secp256k1_surjectionpr | |||
| 244 | 280 | return true; | |
| 245 | 281 | } | |
| 246 | 282 | ||
| 283 | + // Test-only hooks (see sigcache.h). Forward to the anonymous-namespace caches. | ||
| 284 | + void TestComputeEntryRangeProof(uint256& entry, | ||
| 285 | + const std::vector<unsigned char>& proof, | ||
| 286 | + const std::vector<unsigned char>& commitment, | ||
| 287 | + const std::vector<unsigned char>& asset_commitment, | ||
| 288 | + const CScript& script_pub_key) | ||
| 289 | + { | ||
| 290 | + rangeProofCache.ComputeEntryRangeProof(entry, proof, commitment, asset_commitment, script_pub_key); | ||
| 291 | + } | ||
| 292 | + | ||
| 293 | + void TestComputeEntrySurjectionProof(uint256& entry, | ||
| 294 | + const uint256& hash, | ||
| 295 | + const std::vector<unsigned char>& proof, | ||
| 296 | + const std::vector<unsigned char>& commitment, | ||
| 297 | + const std::vector<secp256k1_generator>& vTags) | ||
| 298 | + { | ||
| 299 | + surjectionProofCache.ComputeEntrySurjectionProof(entry, hash, proof, commitment, vTags); | ||
| 300 | + } | ||
| 301 | + | ||
| 247 | 302 | // END ELEMENTS | |
| 248 | 303 | // | |
| Back | FazBrowse Home | New Git URL |
0 commit comments