Compare commits

...

63 Commits

Author SHA1 Message Date
triangles-bot c606253c41 utxosnapshot: build address index when loading a UTXO snapshot (fast-start nodes get balances) [v5.9.17] 2026-06-16 20:37:44 -07:00
triangles-bot b2dfb627cc main: build address index during FastImport (fix-in-place, v5.9.16) 2026-06-16 20:24:26 -07:00
triangles-bot d0a76f8ae2 qt: show Seed Phrase (HD Backup) in the visible Operations menu (v5.9.15)
The HD seed action was only added to the standard Qt menu bar, which the
skinned GUI hides. Add it to menuOperationsRequested() so users can actually
reach Generate / Reveal-for-backup / Restore from the Operations menu.
2026-06-16 16:24:12 -07:00
SamiAhmed7777 cc57c906b4 Merge PR #6: HD seed-phrase wallet + fast-sync checkpoint/snapshot (v5.9.14)
HD wallet (BIP39/BIP32 seed phrases) - daemon + Qt
2026-06-15 20:44:52 -07:00
Sami e80d672833 checkpoints: add 2206004 checkpoint + UTXO snapshot hash (fast new-node sync) 2026-06-15 20:31:02 -07:00
Sami fcdc9a58b0 ci(lint): checkout secp256k1 submodule for clang-tidy (fixes configure) 2026-06-15 19:26:43 -07:00
Sami 514867c5d9 wallet(HD): flush keypool on seed set so getnewaddress yields HD keys immediately 2026-06-15 19:16:03 -07:00
Sami c464e6c59d wallet(HD): Qt UI - Seed Phrase dialog (generate/restore/backup)
Adds HDSeedDialog (Settings > Seed Phrase) with Generate New / Reveal for Backup / Restore from Phrase, driven by new WalletModel HD methods. Restore rescans the chain. Requires wallet unlock via the standard UnlockContext.
2026-06-15 19:16:03 -07:00
Sami 11ed086d1e wallet(HD): native BIP39/BIP32 HD wallet - daemon side
Adds deterministic HD key derivation (path m/44'/2222'/0'/0/i, matching the TRIdock web wallet) wired into CWallet: HD seed stored in wallet.dat (encrypted with the wallet master key when the wallet is encrypted), keypool derived from the seed, and new RPC commands hdnew/hdrestore/hdshow/hdinfo. Crypto core verified standalone against the official BIP39 vector and triWallet.js addresses.
2026-06-15 19:15:43 -07:00
Hermes 5511cfae6b v5.9.14 + pitfall #61 guard: initialize pindexFinalized on startup
ROOT CAUSE of the 2026-06-16 minority-fork reorg:

The v5.9.14 getheaders handler serves headers from pindexGenesisBlock
when a fork peer sends a locator that doesn't match our main chain. The
intent was to help fork peers learn the canonical chain. The bug: this
allows the fork peer to feed us THEIR short chain back via getheaders,
and we accept it because:

1. pindexFinalized is NULL on a fresh restart (the auto-checkpoint code
   in ActivateBestChain() at main.cpp:2459 only sets it when
   !IsInitialBlockDownload(), but a synced daemon restarting with a
   chain tip > 24h stale is considered IBD by the time check at
   main.cpp:1331).

2. With pindexFinalized = NULL, the reorg guard at main.cpp:2198
   short-circuits: 'if (pindexFinalized && pfork->nHeight < ...)'.

3. The fork peer's 3,755-block chain gets accepted, overwriting our
   healthy 2,206,004-block chain.

THE FIX (two parts):

A. init.cpp:1083-1113 — after LoadBlockIndex(), call
   Checkpoints::GetLastCheckpoint(mapBlockIndex) to initialize
   pindexFinalized from the hardcoded checkpoint (block 2,205,000).
   This is a no-op on the first ~10 seconds of the daemon's life
   (during the initial IBD walk), but as soon as we sync past block
   2,205,000, the checkpoint is in mapBlockIndex and pindexFinalized
   is set for the daemon's entire lifetime.

B. main.cpp:4473-4496 — in the getheaders fork-detection branch,
   serve from pindexFinalized->pnext (the block after our last
   finalized checkpoint) instead of pindexGenesisBlock. This is the
   safe equivalent of the v5.9.14 'serve from genesis' logic — the
   peer learns our canonical chain from the most recently finalized
   point forward, and their short fork gets rejected at the reorg
   guard in Reorganize() because the fork point is below
   pindexFinalized.

Combined: a fork peer can no longer drag us below block 2,205,000
because (a) pindexFinalized is always set on a synced restart, and
(b) the reorg guard now sees a non-NULL pindexFinalized and rejects
any fork below it.

Tested manually by simulating a restart with the v5.9.14 binary on
a chain that had been reorged to a minority fork; the new build
refuses the reorg and prints 'STARTUP-CHECKPOINT: pindexFinalized set
to block 2205000' on startup, then 'getheaders: fork detected from
peer ... serving headers from finalized block 2205000' on the first
fork peer's getheaders request.
2026-06-15 18:37:53 -07:00
Sami Ahmed 1dda8b3006 Auto-repair corrupt Tor state file on daemon startup (pitfall #19)
Tor's atomic state-write is: write state.tmp, then rename to state.
If the daemon is killed mid-write (pkill -9, OOM, power loss, disk-full),
the rename can fail and 'state' is left as a regular file instead of a
directory. On next start, Tor's config validator refuses to use it:

  [warn] State file '...' is not a file? Failing.
  [err] set_options: Bug: Acting on config options left us in a broken state. Dying.
  [err] Reading config failed--see warnings above.

The daemon then reports 'Tor failed to start. Triangles requires Tor to
operate.' (the error from the 2026-06-15 TRI-LAPTOP GUI wallet
screenshot) and refuses to come up at all.

Hit before on DNS3 (2026-05-24, fixed by manual 'mv state state.file.bak;
mkdir state' on the operator) and on the laptop today. The fix was always
the same one-liner; this patch makes the daemon do it itself.

Behavior:
- Detect: if tor_data/state exists and is NOT a directory, it's corrupt
- Quarantine: rename to tor_data/state.corrupt-YYYYMMDD-HHMMSS for
  inspection (the user might want to recover the file's contents)
- If rename fails (Windows anti-virus holds the file, etc.), retry with
  remove-then-rename, and as a last resort just remove() so Tor can
  proceed
- Print a clear 'Tor state was a file (corrupt) — quarantined to ...'
  log line so the operator can see it happened
- Tor then recreates state/ as a fresh directory and bootstraps normally

This is a pure-additive change (no existing behavior modified). Builds
clean with the existing C++20 + libtor.a toolchain. No version bump
needed - will roll into the next CI cycle.
2026-06-15 16:29:05 -07:00
Sami Ahmed 68c4e38411 Fix getheaders pnext null bug: serve main chain from genesis, tip-backwards fallback
Two related bugs in the getheaders handler at main.cpp:4441:

1. Locator-mismatch returns 0 headers:
   GetBlockIndex() falls through to pindexGenesisBlock when no locator
   hash matches the main chain. pindexGenesisBlock->pnext is null, so
   the for-loop exits immediately and the peer gets an empty headers
   response. This was the DNS3 headers-first sync stall (pitfall #36):
   'getheaders -1 to 0000...' logged repeatedly.

   Mirror the getblocks handler (line 4387): detect the mismatch, log
   a warning, and serve headers from genesis so the peer can discover
   the canonical chain.

2. Broken pnext chain at any height:
   Even when GetBlockIndex() returns a valid (non-genesis) block, its
   pnext can be null — this happens when LoadBlockIndex() didn't fully
   heal pnext links (e.g., the node was bootstrapped from a snapshot,
   or the chain was interrupted by a crash). On tridock every pnext
   link was null despite 2.2M blocks (the June 11 'Heal pnext links'
   commit addressed a similar symptom for GetKernelStakeModifier() but
   doesn't reach into the getheaders handler).

   Fall back to a tip-backwards walk from pindexBest when pnext is
   null: O(N) but correct, and only triggers on the broken path.

   (References the design in references/getheaders-pnext-fix.md from
   the v5.9.10 deploy notes — that fix was never actually committed,
   only prototyped and dropped during the June 4 git cleanup.)

Bump to v5.9.14 (also embeds the embedded-Tor 0700 fix from ed996c8).

Refs: SKILL.md pitfall #36
2026-06-15 15:43:03 -07:00
Sami Ahmed ed996c8e9d Fix embedded Tor bootstrap (code -1): force 0700 on hidden service dir
Tor's config validator refuses to start a hidden service on any directory
whose permissions are not 0700. The daemon's fs::create_directories() honors
the process umask (0022 on Linux), leaving hidden_service/ at 0755. tor_run_main()
returned -1 with:

  [warn] Permissions on directory .../hidden_service are too permissive.
  [warn] Failed to parse/validate config: Failed to configure rendezvous options.

This was misdiagnosed as a libtor.a build problem (the June 4 rebuild was a
red herring). The real fix is two fs::permissions() calls after create_directories().

Reproduced with a standalone harness linking libtor.a, fixed, SOCKS port 19099
came up in 1 second and Tor began bootstrapping normally.

Also force 0700 on the DataDirectory itself - same validator, same rule.

Refs: SKILL.md pitfall #59
2026-06-15 15:32:28 -07:00
Sami Ahmed cd9e023865 Bump version to v5.9.13 (transparent Qt wallet icon) 2026-06-13 20:49:29 -07:00
Sami Ahmed f273d651ed Make Qt wallet icon transparent (remove white field around triangle) 2026-06-13 20:45:23 -07:00
Sami Ahmed 2ac88b4e0a Add finality checkpoint at 2205000 (anti-fork); v5.9.12 2026-06-13 15:09:44 -07:00
sami7777 c1340441cc Bump version to v5.9.11 2026-06-11 02:57:42 -07:00
sami7777 c99d859781 Fix use-after-move crash in orphan block handling; add -ignoredupstake recovery flag
mapOrphanBlocksByPrev.insert dereferenced pblock2 after std::move'ing it into mapOrphanBlocks - guaranteed null deref (segfault at offset 4) on every orphan block received. Capture hashPrevBlock and the raw pointer before the move.

Also add -ignoredupstake (default off): bypasses the duplicate proof-of-stake rejection so canonical blocks can be imported when a fork twin staking the same outpoint was seen first. Recovery/diagnostic use only.
2026-06-11 01:34:34 -07:00
sami7777 713f69e137 Gate 7-day coin-age soft cap behind activation timestamp
The soft cap (1c068f4, 2026-04-20) shipped without a height/time gate, retroactively invalidating blocks staked earlier with long-aged coins (e.g. coins idle through the 2022-2026 freeze; block f9f976d0 at 2203410 stakes a 3.4-year-old coin). Apply the cap only to stakes at/after 1776000000 (2026-04-12 ~13:20 UTC); historical stakes validate under the rules they were created with.
2026-06-11 01:34:21 -07:00
sami7777 e3f397c7e5 Heal pnext links on active chain at LoadBlockIndex
Persisted hashNext can be stale or zeroed by crash-interrupted reorgs, breaking GetKernelStakeModifier()'s forward walk and silently rejecting valid new PoS blocks ('check kernel failed', hashProof=0). On DNS3 every one of 2,201,458 links was zeroed on disk. Root cause of the 2026-04-24 chain halt. Rebuild the in-memory links from pindexBest at every startup.
2026-06-11 01:34:20 -07:00
Krystie f80fb98f68 Merge master into cpp20-modernization (RPC fix, auto-bootstrap, tor_data fix)
Brings in:
- 79b0c4a: Fix RPC thread crash on bad auth (T001) — adapted to C++20 style
- d0fb2dc: Enable auto-bootstrap for GUI wallets
- 89a480a: Fix tor_data/state directory trap + make -notor work
- 372b252: Fix Windows CI shell for git submodule
- 6c56e41: Init secp256k1 submodule before build

Kept v5.9.9 version (cpp20-modernization's, newer than master's v5.9.7).
CI workflow kept cpp20-modernization's version (submodule init already present).
2026-06-04 18:25:12 -07:00
Krystie 610b61b1b1 Fix chain reorg crash: coinbase timestamp check + corrupted checkpoint hash
CheckBlock() at line 2825 used the heightless FutureDrift() overload,
which hardcodes 90-second drift (post-FORK_HEIGHT_V5_4). During reorgs
from the block-570 fork chain, block 571 (July 2014) has a coinbase
timestamp that legitimately exceeds 90s before block time, causing:
  ERROR: CheckBlock() : coinbase timestamp is too early
  ERROR: Reorganize() : ConnectBlock failed
  -> infinite crash loop (791 iterations recorded)

Root cause: commit a671708 (v5.8.3) tightened drift from 3min to 90sec.
The heightless overload applies this to ALL blocks regardless of height.

Fix: use explicit 10-minute tolerance in CheckBlock (context-free, no
nHeight available). The tight 90-second check is still enforced in
AcceptBlock/ConnectBlock with proper height context.

Also fix corrupted checkpoint hash at block 3935: truncated '07' in
both mainnet and testnet tables during C++20 modernization.
2026-06-04 18:21:43 -07:00
Krystie e8edcd4aa6 Merge remote-tracking branch 'gitea/master' 2026-05-30 23:01:53 -07:00
Krystie 3928f86657 Merge remote-tracking branch 'gitea/master' into cpp20-modernization 2026-05-30 23:00:56 -07:00
Krystie 67d838433d Fix getblocks: serve main chain to fork peers instead of banning them
Build All Platforms / test-linux-unit (push) Failing after 40s
Build All Platforms / test-linux-sanitizers (push) Failing after 40s
Build All Platforms / build-linux-qt (push) Failing after 40s
Build All Platforms / build-linux-daemon (push) Failing after 40s
Build All Platforms / build-windows-qt (push) Has been cancelled
Build All Platforms / build-windows-daemon (push) Has been cancelled
Build All Platforms / build-macos (push) Has been cancelled
Build All Platforms / release (push) Has been cancelled
Build All Platforms / Trigger TRI-PI ARM64 Build (push) Has been cancelled
Fork nodes (DNS3/DNS2 stuck on block 570 chain) send locators that
don't match any main chain block. Instead of disconnecting/ banning
after 3 failed getblocks attempts, this change:

- Serves main chain blocks from genesis when locator has no match
- Resets nIncompatibleGetblocks counter after 10 (so we never ban)
- Fork nodes will receive, validate, and automatically reorg to the
  longer/higher-work main chain once they see it

This fixes the 'no common blocks' deadlock while preserving chain
integrity — only a genuinely longer chain can trigger the reorg.
2026-05-24 22:36:13 -07:00
Krystie e3aeff002c Remove block 570 checkpoint - let nodes sync naturally to main chain
Build All Platforms / test-linux-unit (push) Failing after 40s
Build All Platforms / test-linux-sanitizers (push) Failing after 40s
Build All Platforms / build-linux-qt (push) Failing after 40s
Build All Platforms / build-linux-daemon (push) Failing after 40s
Build All Platforms / build-windows-qt (push) Has been cancelled
Build All Platforms / build-windows-daemon (push) Has been cancelled
Build All Platforms / build-macos (push) Has been cancelled
Build All Platforms / release (push) Has been cancelled
Build All Platforms / Trigger TRI-PI ARM64 Build (push) Has been cancelled
2026-05-24 21:54:42 -07:00
Krystie 09ccd4339c IBD stall fix: restore IsInitialBlockDownload() time check, add block 570 checkpoint, fix version handler IBD bypass 2026-05-24 15:40:29 -07:00
sami7777 03aa38f1b4 Promote NewIterator() override to public in both chain DB backends
The base class CTxDBBase declares NewIterator() public, but both
backends overrode it in their protected: section. That narrowed the
static access through the derived type, so the migration utility
(which holds concrete CTxDB / CRocksTxDB instances) couldn't call it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 01:01:44 -07:00
sami7777 9389a883f1 Wire CSyncManager + LevelDB->RocksDB chain DB migration
CSyncManager extracts the headers-first IBD planner from main.cpp into
its own translation unit. main.cpp loses ~570 lines of file-scope state
and helper functions; the headers handler, block-delivery latency
tracking, stall-recovery, and per-peer Tick cadence now route through
g_syncManager.

MaybeMigrateLevelDbToRocksDb() is now reachable via -migratechaindb /
-migratechaindbforce in init.cpp. Reads from <datadir>/txleveldb and
writes byte-for-byte identical records into <datadir>/rocksdb via a
new CRocksTxDB::WriteRawRecordForMigration() shim over WriteRaw.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 00:44:08 -07:00
sami7777 31fa26f03a Drop checkpoints > 2,000,000
Trim mainnet and testnet checkpoint tables to the 2,000,000 entry.
Clears the snapshot-hash entry at 2,203,594 since its corresponding
checkpoint is now gone (per the invariant noted in the comment block).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 03:07:58 -07:00
sami7777 ce96d278cd Bump client version to v6.0.0
Internal refactor milestone for the C++20 modernization series.
No protocol or on-disk format change (version.h untouched).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 03:07:57 -07:00
Krystie 7166b76bad ci: fix test-linux-sanitizers submodule checkout 2026-05-10 19:36:16 -07:00
Krystie 540db0e210 Fix wallet logging and Qt min-fee enum regressions 2026-05-10 19:18:47 -07:00
Krystie 59b75476ca ci: autonomous fix iteration 2
Generated by triangles-ci-loop.sh on 2026-05-10 18:20:35 PDT. See /var/log/triangles-ci-loop.log.
2026-05-10 18:20:35 -07:00
Krystie 0029b34698 ci: autonomous fix iteration 1
Generated by triangles-ci-loop.sh on 2026-05-10 18:02:40 PDT. See /var/log/triangles-ci-loop.log.
2026-05-10 18:02:40 -07:00
Sami 8e03e89764 ci: trigger Build All Platforms on push to cpp20-modernization 2026-05-10 17:47:35 -07:00
Krystie 3b1850af9d ci: autonomous fix iteration 1
Generated by triangles-ci-loop.sh on 2026-05-10 12:24:48 PDT. See /var/log/triangles-ci-loop.log.
2026-05-10 12:24:48 -07:00
Krystie 223029785c Fix Qt wallet model ownership calls 2026-05-10 02:52:39 -07:00
Krystie ce8be45ea5 Fix reserve key wallet pointer ownership 2026-05-10 02:40:47 -07:00
Krystie e6d8c6dbfe Fix REST path parsing redeclarations 2026-05-10 02:33:40 -07:00
Krystie 74ec53040c Fix duplicate auto declaration regressions 2026-05-10 02:22:37 -07:00
Krystie e251a85d7a Fix string GetArg overload ambiguities 2026-05-10 02:14:17 -07:00
Krystie a16533f11b Fix accounting test wallet pointer usage 2026-05-10 02:03:52 -07:00
Krystie b979f7ae7d Fix GetArg overload ambiguity for pid file 2026-05-10 01:53:32 -07:00
Krystie b1e9878849 Fix script OpenSSL and restrict warning regressions 2026-05-10 01:46:39 -07:00
Krystie b47fa91d6c Fix script signature const-correctness regression 2026-05-10 01:38:42 -07:00
Krystie e9e4a0ca82 Fix remaining walletdb and keystore C++20 issues 2026-05-10 01:21:06 -07:00
Krystie f5e2ce5ca9 Fix OpenSSL 3 and fold-expression warning regressions 2026-05-09 21:27:36 -07:00
Krystie cdbbbb5316 Fix wallet and bigint C++20 build regressions 2026-05-09 20:35:59 -07:00
Krystie c5967e9995 Suppress OpenSSL 3 SHA256 deprecation warnings 2026-05-09 20:17:27 -07:00
Krystie 5f61ed8fcb Fix C++20 type-tag and string timestamp regressions 2026-05-09 19:32:44 -07:00
Krystie c3e4a456d8 ci: fetch submodules in GitHub Actions 2026-05-09 03:30:53 -07:00
sami7777 080942b49d C++20 Round 7: [[nodiscard]] on critical bool functions
- Add [[nodiscard]] to validation functions whose return value must be checked:
  CTransaction::IsStandard, CheckTransaction, ConnectInputs, AcceptToMemoryPool
  CBlock::ConnectBlock, AcceptBlock, IsInitialBlockDownload
  IsStandard, IsMine (3 overloads), SignSignature (2), VerifyScript, VerifySignature
  CWallet::IsMine (3 overloads)
2026-05-08 23:54:05 -07:00
sami7777 1220168faf C++20 Round 6: range-for loops, throw() -> noexcept
- Convert 8 index-based for loops to range-for in main.cpp:
  CheckTransaction vout, GetValueIn vin, GetP2SHSigOpCount vin,
  ConnectInputs first pass vin, ConnectBlock vtx, Reorganize vConnect,
  block.vtx in CBlock::AcceptBlock
- Convert 4 loops in main.h: CTransaction::ToString vin/vout, CBlock::print vtx/vMerkleTree
- Convert checkqueue.h worker loop to range-for
- Convert script.cpp bitwise NOT loop to range-for
- throw() -> noexcept on 8 allocator constructors/destructors (C++17 removed throw())
2026-05-08 23:43:27 -07:00
sami7777 4b8d5ab8b1 C++20 Round 5: typedef -> using, IMPLEMENT_SERIALIZE macro cleanup
- Convert 15 typedef declarations to C++11 using aliases across 12 files:
  script.h (valtype, CTxDestination), serialize.h (CSerializeData),
  keystore.h (KeyMap, ScriptMap, CryptedKeyMap), sync.h (CCriticalSection,
  CWaitableCriticalSection), sync.cpp (LockStack), key.h (CPrivKey, CSecret),
  crypter.h (CKeyingMaterial), allocators.h (SecureString), main.h (MapPrevTx),
  wallet.h (mapValue_t, removed duplicate), miner.cpp (TxPriority),
  kernel.cpp (MapModifierCheckpoints)
- Remove redundant duplicate mapValue_t typedef in wallet.h
- IMPLEMENT_SERIALIZE macro: replace assert() warning suppression with
  [[maybe_unused]] attributes on fGetSize/fWrite/fRead/nSerSize
2026-05-08 23:08:40 -07:00
sami7777 9d80ddb6ac C++20 Round 4: enum class TxnOutType, remove boost string alg, boost::int64_t -> int64_t
- txnouttype -> enum class TxnOutType (NonStandard, PubKey, PubKeyHash, ScriptHash, MultiSig)
  Updated script.h/cpp, main.cpp, wallet.cpp, rpcrawtransaction.cpp, rpcwallet.cpp, multisig_tests.cpp
- boost::int64_t/uint64_t -> int64_t/uint64_t across all RPC files (7 files, 52 replacements)
- Replace all boost string algorithm includes with std:: equivalents:
  - Added TrimString(), ToLower(), ReplaceAll(), SplitString(), JoinStrings() to util.h
  - boost::trim -> TrimString() (bootstrap.cpp, trianglesrpc.cpp, rest.cpp)
  - boost::to_lower -> ToLower() (netbase.cpp, trianglesrpc.cpp)
  - boost::split/is_any_of -> SplitString() (rpcdump.cpp, trianglesrpc.cpp, rest.cpp)
  - boost::replace_all -> ReplaceAll() (main.cpp, wallet.cpp)
  - boost::algorithm::starts_with/ends_with -> std::string::starts_with/ends_with (rpcdump.cpp, smessage.cpp)
  - boost::algorithm::istarts_with -> case-insensitive lambda (init.cpp, qtipcserver.cpp)
  - boost::algorithm::join -> JoinStrings() (util.cpp)
- boost::bind -> lambda in trianglesrpc.cpp async_accept handler
- IsHex() parameter: const string& -> string_view
2026-05-08 22:26:31 -07:00
sami7777 150828b806 C++20 modernization: nullptr, constexpr, smart pointers, thread safety, enum class
- Replace ~320 NULL occurrences with nullptr across 47 files (-184 net lines)
- static const -> constexpr for version, coin, utility constants
- Collapse 9 PushMessage overloads into 1 variadic template with fold expressions
- Convert boost::array -> std::array, boost::type_traits -> std:: equivalents
- pwalletMain, pScriptCheckQueue, pScriptCheckThreads -> unique_ptr
- mapOrphanBlocks values: raw CBlock* -> unique_ptr<CBlock>
- Fix data races: add locks to wallet registration, pindexBest reads, mempool exists
- pwalletdbEncryption: raw new/delete -> local unique_ptr, remove exit() calls
- PoS reward overflow: CBigNum intermediate for nCoinAge * nRewardCoinYear
- memset -> OPENSSL_cleanse for secure zeroing
- Fix const-cast UB in SetMerkleBranch
- Log silent catch(...) blocks instead of silently swallowing
- Enum class: GetMinFeeMode, WalletFeature
- std::string_view for 8 utility function parameters
- Range-for with structured bindings: 63 iterator loops modernized
- std::make_pair -> brace init: 35 sites
- Delegating constructors: CWallet, CBlockIndex
- Merkle tree caching, std::array for GetMedianTimePast
- CScript copy ctor -> = default, operator!= -> = default
2026-05-08 21:34:24 -07:00
Krystie b4308e42ad Smoke-test the Krystie loop runner
Krystie Gate / Static gate (red-list / test-first / no-clearnet) (push) Successful in 35s
Krystie Gate / Build + ctest (push) Successful in 7m11s
Krystie Gate / Auto-merge to master (push) Successful in 28s
This issue was created to exercise the autonomous runner end-to-end.

**Acceptance:** runner picks this up, demo worker appends a line to docs/krystie-runner-log.md, branch krystie-wip/triangles_v5-1 is pushed, gate fast-forwards to master, this issue auto-closes.

Closes #1
Refs: krystie-wip/triangles_v5-1
2026-04-28 23:57:31 -07:00
Sami c4656ac244 fix: gate auto-merge — use git push instead of PATCH /branches/master
Gitea PATCH /repos/{owner}/{repo}/branches/{branch} is for renaming branches, not for moving refs; it always returned failure even when master had not diverged. Replace with a plain git push (token in extra header) which fast-forwards iff the update is FF-clean — same safety, correct mechanism.
2026-04-28 23:56:51 -07:00
sami7777 2da1c039a8 Gate: accept Krystie subkey ID (git %GK returns subkey not primary) 2026-04-28 22:42:22 -07:00
sami7777 2b701f6640 Gate: handle force-push orphaned-history (fall back to head-only inspection) 2026-04-28 22:39:14 -07:00
sami7777 de4498b8eb Fix gate: trust Krystie public key after import so %GK verifies 2026-04-28 22:35:51 -07:00
sami7777 815cc02aa9 Bootstrap Krystie autonomous gate (manual one-time setup)
Adds the gate workflow + check script + Krystie public key under .gitea/.
This commit is intentionally unsigned so the gate treats it as an admin
bootstrap rather than a Krystie commit (which the gate would otherwise
require to land via krystie-wip/* + auto-merge).

After this, master branch protection will be enabled requiring the
Krystie Gate workflow to pass on all future pushes. Krystie will push
to krystie-wip/<task-id> branches and the workflow auto-merges on green.

See: krystie-buildout/workflows/* in the krystie repo for sources.
2026-04-28 21:42:57 -07:00
90 changed files with 3879 additions and 2034 deletions
+65
View File
@@ -0,0 +1,65 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGnxdoUBEACaICSRk5Clg4kI5IubMXnXLbsSWzi0TKIpqh4Tqgl2k1bgSxda
tuBabHcsaw6Kpo96CJl9aZ63VIrEhCSdirGm/wWlbnTvm6cK4EDucGgS4BdEfm9B
Lw2c+iTjuJqJt2HLbRkZmF8qHy0Mo1DjsjbWUiwIP62RkuxCNuW2Wl9euak504UW
ZTFB9f3Bu1C6rknsWQ0VR5HJwWN4UrVMukZhvlzLRjKgW7W2XchSXUIAe7b0/5jo
pFB30pwxbaBIoeJu8AHYnzBYRThp0WbDTC/LK5FSnSgG751jOtkbheRNGjO65a2L
gkaclxo1NUIIu+WqdBtTbpUQM7UEd50FOXxUgq/xJhGujNMJyOMMPEzfJ+kP9pD4
p+gkNCLLgvT+gu1PnF0iTIAb4qggHGzZGRgc5lTxC28XEud0DAx+Pdcdf/nlQTsu
AOjZZgiiLIjwJZo/RYwId1Wh+LmtYZqVZ6j4vqqaXXPADpN40LGyUo376+oVSn77
1w2j1CWSmTEPaq4KmvTvnTvFfbeXkKckmUziBYwqZI0uA2xE6ShNUaAS4kdIaZhO
Bb3t9xrwu2QAR1rRlNTCChOyNbauvo32GLRnXg5BXYTBsmMU/QHe6EBJsycq/IHl
2yNPQUtynxzkDZ9OYrwbZaTZOCJK0pHwm4HUmV3rPiEPXUKJXXDojWQYpwARAQAB
tHlLcnlzdGllIFRyaWFuZ2xlcyBSZWxlYXNlIChBdXRvbm9tb3VzIHJlbGVhc2Ug
c2lnbmluZyBrZXkgZm9yIHRyaWFuZ2xlc192NSkgPGtyeXN0aWUtdHJpYW5nbGVz
LXJlbGVhc2VAZG5zMi5zYW1pLnRhaWxuZXQ+iQJYBBMBCgBCFiEEUjqBgz63IBVz
4e/h3PJXmWgQeYQFAmnxdoUDGy8EBQkDwmcABQsJCAcCAiICBhUKCQgLAgQWAgMB
Ah4HAheAAAoJENzyV5loEHmEPm0P/3y2Y5Y1rhgSj6yN/1PuXhpp1sNqXBOJZxTW
uUx/4LUqLgqbtFC0fR4BwpTYEkGGaofi0/95sPwKu0jmVR6hJ+8Omk/4TMRmXUYq
JUTA0/xzj9sOndaqiwRY3Y/YO/ytahL89y8xl5cYSaOOwLI/f9xo8pq1t20Iiuiw
kcaUBRQgpTVMI49VcXwrEUMnjV9cldGqql8v7CSKds5rRxQgT8ifaC6euTWxK0Tn
5Yu/wnBd+akU5/bcI8PEp5VyUyAJMZJPZ6mUqriWXlnhiUj0NawEKtfG9qlkMixL
5ujz9lu/9MvFUYC4QSvcd1O3k9MJ6T4Yk/uEygEca8Y/3DcccWRMHjW2Ah+ewhHE
yHy0tctzCe7pco+jfB7zicKv0bjXarvwBZ43e5F/zG5PMpo0XAS9EkEUV+/9BJ38
jBHvzqwXsYTnxS0hgOSONJk9Cc6i0NN1ex3rPOrYvBvHWZ+9n3AU2taUljuypDGO
RweCHsFMYGx/oOI94bD7wTeVey0tAZ+3Urz6T5qY5SmNKiwZ5NtbYo0Mp8r5DdPJ
N9KtXtaDMPI/rORjl1Ad9xhDbGMCr7EH9SjTU+z51me31/ZU58jICGlvm3/JDcb5
CAWyDppvW0ul9yqo1fecSi3w7m2sI+4F+tj8oLFmO+5rQw85F4LPqjVVMbUUkoAH
udtoU3Y8uQINBGnxdoUBEACtFpgwuwEZqxbsfmL+uBxHnxSSRm2vlQc7HRtQG6Nu
Tg1x4s9xFO6kNkcslPgZx9XSvFkPt1RUCNViTYE34UoOfkBs+aNkw4ztwuKGt/AS
CZFRX99yBx7P0kiV4Nt/Cj3oQBtEXQixMmGK4+N0WBskV/QxRFA7hl+ZQBeEFsYP
15UyjX2h6HFRYTSPKufEmtE/OkO9dg3fyxTvZ3+1o3eWWjT4VReX4jvmzXn3RNP1
BwuAy+iwmnqUBcuEZ0qQiT/+oRLCHOFLCAjVoSsPY9WJfF67XpDb2noV/0RqltMD
jUc/MT8Bxn/y8qHKvQuyPms/YO5jMI7q+/D1eayO4R48qhsMVp6Rjb31xalMWT2W
rwQg1XaFG80vUisbfX6CU0sH34tWQkqAL7AiwradPtwB0Sn60Em5UgHdWQ7rkd+h
mFOUjYi3Q1hOuPQNuzDK51n5sv8qOIrfghR0F2AtRkpbhBYM9435U+JkcZTjJ6wp
WYLBTAys4qo9MnL18Z4byaw4e122eBgI3/UOvG+7C7wIAwmiDvnYzqErz7iOmuTe
+cgdWYmLFvkfx8P6Ka+6likSV4ZY/ASP4Uo/gTspatwqHApAmphfVEGwm0/wKMl2
Br+zuZZ8RJ1GxahwJ1oo3uuGjIQjGNplh2wHVvbsfg4mlFKDbShdJ5adtx/E6BrT
NQARAQABiQRyBBgBCgAmFiEEUjqBgz63IBVz4e/h3PJXmWgQeYQFAmnxdoUCGy4F
CQPCZwACQAkQ3PJXmWgQeYTBdCAEGQEKAB0WIQRpE+E2EPaYGDQpziDC3GBhjIWh
WQUCafF2hQAKCRDC3GBhjIWhWQYID/0Ru2U9rLatIAjoSWI6TMFaOaxHf1NAsTcz
fPRbFNxx0d4ByjfjLlrfnDpQXsFpMa6/BpQ1Ps1ApW+wQsuHXxj/jdZVSi5f/sOT
XKZq/MRZu8enA1foj0b6sJ13ZWY0iIWmIeK8NWuNBFWz2QTjRie2hqoOTR+Hy43r
gRMlzPaXNoeD2UuvhoDphH2g2OWcppxd2b1yk7W9kh0CgvXXg4cPee71LmXLZMoL
GJcmtSkU24fiwa95TSk2J5qQ3voP5Knk8e/VgGmOSUoUzr+O5N6tEO2KPVr3bsFt
8zKHEyuddDYUju4U2Fl+xq4yJCYX3h6AKyh/c3bOAGp4f3zs62XPjn9RIXlTH9Lw
Vp97pJRzAEYzXRGXfGJRz54hQzft1L+BkhqWpVwzxI1fnflpVghahHOIoa0bnpyH
ycxxvkGY6o5TS5Ymqf4yry/4G+C64kX2GlBgmN2I2+UJ3z/cyEqY4XVMGk4S7uLq
d0eKrA2ZaSHUce0F/gGpMynxGFP+BNlfNBcSwzgBbnvcyFhOtls4LvTAcLmyBpjM
gEugtkskDSxJd/HcnTcFF5P9UcVPdD7vg7tlUXQ37AvbeppFC4pFbxYK01SOYk+W
nXH/Mq1XkFFcArVtsL1octAWuaqn8M/5kXnKvhw/TCBNPfQ7Kljx1V65kErMXNl2
F/cJXWQKCXPtD/92EXa9uvIxCINwxyZidwEvqx1xpBTIDDdYvDt8ZXHr957xpiaz
ls3aHy0mMUGigzVEL0AcPToBEudEzy+z1pB0y23znveycDZRTRsGnDwLrdb9eqTu
JDViRtB6WBASGsU3XHMYFietvEukmqJj55KCDl5YapZDKUb1iraERJ72PH9xk3C7
501Cklfe+GM8VBymwApOjWPLw1cIxVOL/Ex9ADsVMYDubAVh0LnqvDTg8e8bv4gu
BhyC2AXsQIUZ9HtixfvLZ6sdsPjstlQj+ZinpTHWthx52jrfcRYOo32cE06BpR3U
bQ+mjn6orzZ7Iq5p6aejukCddvlSX381vMaLf1/FGzmu/9f52p7uTLxU7N8sEcqq
PlkdRYatwWDeKuGpYVqmXuPvAaPD/sfH6zw0O5JjcNhb5KqTMjcV7IXV+V7QU2F5
iH5eYepAFf5uctffFMlCZ2YtCLlISMxHWLLqupIlu/JumTLcUjXUpOMV/sp+v6gD
66yx5QQWtVdYT9dYW+EUybjuWlS85T9DJVrPx5GiQfKjgFzuyuEvsbExzVBOwsBP
o/pPUWyBNSI6YVrm329U7ybAuDdnTveaMtIxRneN8mM9lhXNWpb8UpvSGnMP0lLI
tx58dQjEl3lbis897KDgzHy2pGKQDcvLdj14/xpfjeTWHI6Ut3mZylIKWg==
=zWaw
-----END PGP PUBLIC KEY BLOCK-----
+262
View File
@@ -0,0 +1,262 @@
#!/usr/bin/env python3
"""Krystie Gate — static check stage of the CI gate.
Runs inside the Gitea Actions runner. Inspects all commits that were just
pushed to a krystie-wip/* branch and rejects if any violates the gate rules.
Decision per commit:
* If signed by Krystie's GPG key (fingerprint DCF2579968107984), apply the
full per-repo gate.
* If signed by a different key OR unsigned, allow (Sami's authority).
Per-repo enforcement:
* triangles_v5 : red-list (consensus paths) + test-first + no-clearnet
* triangles-explorer, triangles-api, tridock-web-wallet, sami-chat, tri-pi:
test-first only
* homebrew-triangles: formula syntax check only
Outputs:
* On reject, prints REJECTED lines to stderr and exits 1.
* On accept, sets `is_krystie_commit` GH-actions output to true/false.
"""
from __future__ import annotations
import os
import re
import subprocess
import sys
from dataclasses import dataclass
from pathlib import Path
# Krystie's GPG identity. We accept both the primary long-ID and the
# signing subkey because `git log %GK` returns the subkey that was actually
# used to sign, not the primary. The full primary fingerprint is also
# included so a paranoid future check can validate the chain.
KRYSTIE_PRIMARY_FP = "523A81833EB7201573E1EFE1DCF2579968107984"
KRYSTIE_KEY_IDS = {
"DCF2579968107984", # primary long-ID
"C2DC60618C85A159", # signing subkey long-ID
}
RED_LIST_TRIANGLES_V5 = [
re.compile(r"^src/main\.(cpp|h)$"),
re.compile(r"^src/validation.*"),
re.compile(r"^src/kernel\.(cpp|h)$"),
re.compile(r"^src/checkpoints\.(cpp|h)$"),
re.compile(r"^src/consensus/"),
re.compile(r"^src/protocol\.(cpp|h)$"),
re.compile(r"^src/net\.(cpp|h)$"),
re.compile(r"^src/netbase\.(cpp|h)$"),
re.compile(r"^src/net_bootstrap\.(cpp|h)$"),
re.compile(r"^src/chainparams.*"),
re.compile(r"^src/clientversion\.h$"),
re.compile(r"^src/key\.(cpp|h)$"),
re.compile(r"^src/keystore\.(cpp|h)$"),
re.compile(r"^src/onionseed\.h$"),
re.compile(r"^contrib/seeds/"),
re.compile(r"^contrib/devtools/release.*"),
re.compile(r"^doc/release-process\.txt$"),
]
TEST_DIRS = {
"triangles_v5": ["src/test/", "test/"],
"triangles-explorer": ["src/__tests__/", "tests/", "test/"],
"triangles-api": ["test/", "__tests__/", "tests/"],
"tridock-web-wallet": ["test/", "__tests__/", "tests/"],
"sami-chat": ["test/", "__tests__/", "tests/"],
"tri-pi": ["test/", "tests/"],
"homebrew-triangles": [],
}
SOURCE_EXTS = {
"triangles_v5": {".cpp", ".h", ".c"},
"triangles-explorer": {".ts", ".tsx", ".js", ".svelte"},
"triangles-api": {".js", ".ts"},
"tridock-web-wallet": {".ts", ".tsx", ".js", ".svelte", ".vue"},
"sami-chat": {".ts", ".tsx", ".js", ".svelte", ".vue"},
"tri-pi": {".py", ".sh", ".ts", ".js"},
"homebrew-triangles": set(),
}
RED_LIST_REPOS = {"triangles_v5"}
PEER_CONFIG_PATHS = [
re.compile(r"^contrib/seeds/"),
re.compile(r"^src/chainparams.*"),
re.compile(r".*triangles\.conf(\.example)?$"),
]
@dataclass
class GateResult:
ok: bool
reason: str = ""
def repo_name() -> str:
repo = os.environ.get("GITHUB_REPOSITORY", "")
return repo.split("/", 1)[1] if "/" in repo else repo
def commit_signer(sha: str) -> str | None:
try:
out = subprocess.run(
["git", "log", "-1", "--format=%GK", sha],
check=True, capture_output=True, text=True,
).stdout.strip()
return out or None
except subprocess.CalledProcessError:
return None
def is_krystie_commit(sha: str) -> bool:
fp = commit_signer(sha)
if not fp:
return False
# Accept any key ID we know belongs to Krystie. `git log %GK` returns the
# signing subkey, so we have to whitelist both primary and subkey.
return any(fp == known or known.endswith(fp) for known in KRYSTIE_KEY_IDS)
def commits_in_push() -> list[str]:
before = os.environ.get("GITHUB_BEFORE", "")
sha = os.environ.get("GITHUB_SHA", "")
if not sha:
return []
if not before or set(before) == {"0"}:
# New branch — only inspect the head commit (don't walk history)
return [sha]
# On force-push, `before` may have been orphaned and is unreachable in the
# checked-out repo. `git rev-list before..sha` then exits 128. Fall back
# to inspecting the new head only — that's the safest guarantee we can
# make about what just landed.
try:
out = subprocess.run(
["git", "rev-list", f"{before}..{sha}"],
check=True, capture_output=True, text=True,
).stdout
return [c for c in out.split() if c]
except subprocess.CalledProcessError:
return [sha]
def changed_files(sha: str) -> list[str]:
out = subprocess.run(
["git", "diff-tree", "--no-commit-id", "--name-only", "-r", sha],
check=True, capture_output=True, text=True,
).stdout
return [f for f in out.split("\n") if f]
def commit_diff_text(sha: str, paths: list[str]) -> str:
if not paths:
return ""
out = subprocess.run(
["git", "show", "--no-color", sha, "--"] + paths,
check=True, capture_output=True, text=True,
).stdout
return out
def red_list_check(repo: str, files: list[str]) -> GateResult:
if repo not in RED_LIST_REPOS:
return GateResult(True)
for f in files:
for pat in RED_LIST_TRIANGLES_V5:
if pat.match(f):
return GateResult(False, f"red-list violation: '{f}' is consensus/critical-path; needs Sami review (open red-list-labeled issue)")
return GateResult(True)
def _is_test_path(f: str, test_dirs: list[str]) -> bool:
return any(f.startswith(d) for d in test_dirs) or "/test/" in f or "/tests/" in f or "/__tests__/" in f
def test_first_check(repo: str, files: list[str]) -> GateResult:
src_exts = SOURCE_EXTS.get(repo, set())
test_dirs = TEST_DIRS.get(repo, [])
if not src_exts or not test_dirs:
return GateResult(True)
src_changed = any(any(f.endswith(e) for e in src_exts) and not _is_test_path(f, test_dirs) for f in files)
test_changed = any(_is_test_path(f, test_dirs) for f in files)
if src_changed and not test_changed:
return GateResult(False, f"test-first violation: source changed without paired test; expected test under {test_dirs}")
return GateResult(True)
def no_clearnet_check(repo: str, sha: str, files: list[str]) -> GateResult:
if repo != "triangles_v5":
return GateResult(True)
peer_files = [f for f in files if any(p.match(f) for p in PEER_CONFIG_PATHS)]
if not peer_files:
return GateResult(True)
diff = commit_diff_text(sha, peer_files)
for line in diff.split("\n"):
if not line.startswith("+") or line.startswith("+++"):
continue
body = line[1:].strip()
if re.search(r"\b(addnode|seednode|connect)\s*=", body, re.IGNORECASE):
if ".onion" not in body.lower():
return GateResult(False, f"no-clearnet: added peer/seed without .onion: {body[:120]}")
if re.match(r"^\s*(\d{1,3}\.){3}\d{1,3}\b", body) or re.match(r"^\s*[0-9a-fA-F:]{4,}\b", body):
return GateResult(False, f"no-clearnet: clearnet address added: {body[:120]}")
return GateResult(True)
def gate_commit(repo: str, sha: str) -> list[str]:
files = changed_files(sha)
failures = []
for check, args in [
(red_list_check, (repo, files)),
(test_first_check, (repo, files)),
(no_clearnet_check, (repo, sha, files)),
]:
r = check(*args)
if not r.ok:
failures.append(f"commit {sha[:12]}: {r.reason}")
return failures
def emit_output(name: str, value: str):
out_file = os.environ.get("GITHUB_OUTPUT", "")
if out_file:
with open(out_file, "a") as fh:
fh.write(f"{name}={value}\n")
def main() -> int:
repo = repo_name()
if not repo:
print("ERROR: GITHUB_REPOSITORY not set", file=sys.stderr)
return 2
commits = commits_in_push()
if not commits:
print("No commits to inspect", file=sys.stdout)
emit_output("is_krystie_commit", "false")
return 0
krystie_count = 0
all_failures: list[str] = []
for sha in commits:
if not is_krystie_commit(sha):
print(f" {sha[:12]}: not Krystie-signed (allow)")
continue
krystie_count += 1
print(f" {sha[:12]}: Krystie-signed; running gate")
failures = gate_commit(repo, sha)
all_failures.extend(failures)
emit_output("is_krystie_commit", "true" if krystie_count > 0 else "false")
if all_failures:
print(f"\n[KRYSTIE GATE] REJECTED on {repo}:", file=sys.stderr)
for f in all_failures:
print(f" - {f}", file=sys.stderr)
return 1
print(f"[KRYSTIE GATE] PASS on {repo} ({krystie_count} Krystie commit(s) inspected, {len(commits) - krystie_count} non-Krystie)")
return 0
if __name__ == "__main__":
sys.exit(main())
+132
View File
@@ -0,0 +1,132 @@
name: Krystie Gate
# Runs on every push to krystie-wip/* branches.
# Static checks first (cheap), then build + tests.
# If everything green AND the commit is Krystie's, fast-forwards master.
# Sami's pushes (admin) bypass this entire flow — he goes direct to master.
on:
push:
branches:
- 'krystie-wip/**'
jobs:
static-gate:
name: "Static gate (red-list / test-first / no-clearnet)"
runs-on: ubuntu-latest
outputs:
is_krystie_commit: ${{ steps.gate.outputs.is_krystie_commit }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Import Krystie public key (for verification)
run: |
mkdir -p ~/.gnupg && chmod 700 ~/.gnupg
if [ -f .gitea/krystie-release.pub.asc ]; then
gpg --import .gitea/krystie-release.pub.asc
# Mark the key as ultimately trusted so `git log %GK` will consider
# signatures valid. Without this, %GK returns empty and the gate
# treats Krystie's commits as unsigned, defeating the whole point.
FP=$(gpg --list-keys --with-colons | awk -F: '/^fpr:/ {print $10; exit}')
echo "${FP}:6:" | gpg --import-ownertrust
echo "Imported and trusted Krystie public key: ${FP}"
# Configure git to call gpg for verification (it does by default,
# but explicit doesn't hurt) and not to require signed-by-default.
git config --global gpg.program gpg
else
echo "WARN: .gitea/krystie-release.pub.asc not found — gate will treat all commits as non-Krystie (i.e. allow)"
fi
- name: Run gate
id: gate
env:
GITHUB_REF: ${{ github.ref }}
GITHUB_SHA: ${{ github.sha }}
GITHUB_BEFORE: ${{ github.event.before }}
run: |
python3 .gitea/krystie_gate.py
build-and-test:
name: "Build + ctest"
needs: static-gate
runs-on: ubuntu-latest
if: ${{ needs.static-gate.result == 'success' }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Install build deps
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential cmake ninja-build pkg-config \
libssl-dev libboost-all-dev libdb++-dev libleveldb-dev \
librocksdb-dev libevent-dev libsodium-dev \
libsecp256k1-dev || true
# Some packages may not be available; the C++20 / RocksDB modernization
# is in flight, so missing deps are tolerable for v1 of the gate.
- name: Configure (daemon-only, no Qt)
run: |
mkdir -p build && cd build
cmake .. -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_QT=OFF \
-DBUILD_TESTS=ON \
-DBUILD_ROCKSDB=OFF \
|| (echo "::warning::CMake configure failed — likely WIP modernization. Allowing build skip for v1." && exit 0)
- name: Build
run: |
if [ -f build/build.ninja ]; then
cd build && ninja -j$(nproc) 2>&1 | tail -100 || (echo "::warning::Build failed — flagging for Sami review" && exit 1)
else
echo "::warning::No build.ninja produced; skipping for v1"
fi
- name: ctest
run: |
if [ -f build/CTestTestfile.cmake ]; then
cd build && ctest --output-on-failure -j$(nproc) || exit 1
else
echo "::warning::No ctest produced; skipping for v1 — Krystie should add tests in src/test/"
fi
auto-merge:
name: "Auto-merge to master"
needs: [static-gate, build-and-test]
runs-on: ubuntu-latest
if: ${{ needs.static-gate.result == 'success' && needs.build-and-test.result == 'success' }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.KRYSTIE_GITEA_TOKEN }}
- name: Fast-forward master to this branch
env:
GITEA_TOKEN: ${{ secrets.KRYSTIE_GITEA_TOKEN }}
BRANCH: ${{ github.ref_name }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
# The wip branch is master + N Krystie commits. A plain push with
# the wip sha onto refs/heads/master succeeds iff the update is a
# fast-forward — which is exactly the safety we want. (Earlier
# versions called PATCH /branches/master which is Gitea's branch-
# rename endpoint, not a ref-update endpoint, and always failed.)
REPO="${GITHUB_REPOSITORY}" # owner/name
GIT_URL="http://localhost:3030/${REPO}.git"
git -c "http.extraHeader=Authorization: token ${GITEA_TOKEN}" \
push "${GIT_URL}" "${SHA}:refs/heads/master" \
&& echo "Master fast-forwarded to ${SHA:0:12}" \
|| (echo "::error::Fast-forward push refused — master has likely diverged" && exit 1)
# Clean up the wip branch via the same push channel (delete = empty source).
git -c "http.extraHeader=Authorization: token ${GITEA_TOKEN}" \
push "${GIT_URL}" ":refs/heads/${BRANCH}" \
&& echo "Cleaned up wip branch ${BRANCH}" \
|| echo "::warning::Could not delete wip branch (it'll get pruned later)"
+15 -45
View File
@@ -2,7 +2,7 @@ name: Build All Platforms
on:
push:
branches: [master]
branches: [master, cpp20-modernization]
tags: ['v*']
pull_request:
branches: [master]
@@ -13,13 +13,9 @@ jobs:
runs-on: ubuntu-22.04
continue-on-error: true
steps:
- name: Checkout (with history for submodule)
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init submodules
run: git submodule update --init --recursive
submodules: recursive
- name: Install dependencies
run: |
@@ -59,13 +55,9 @@ jobs:
# and BDB until they're fixed file-by-file.
SAN_FLAGS: "-fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=undefined -fno-sanitize=alignment,signed-integer-overflow,vptr"
steps:
- name: Checkout (with history for submodule)
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init submodules
run: git submodule update --init --recursive
submodules: recursive
- name: Install dependencies
run: |
@@ -99,14 +91,9 @@ jobs:
run:
shell: msys2 {0}
steps:
- name: Checkout (with history for submodule)
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init submodules
run: git submodule update --init --recursive
shell: bash
submodules: recursive
- uses: msys2/setup-msys2@v2
with:
@@ -257,14 +244,9 @@ jobs:
run:
shell: msys2 {0}
steps:
- name: Checkout (with history for submodule)
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init submodules
run: git submodule update --init --recursive
shell: bash
submodules: recursive
- uses: msys2/setup-msys2@v2
with:
@@ -327,13 +309,9 @@ jobs:
build-linux-qt:
runs-on: ubuntu-22.04
steps:
- name: Checkout (with history for submodule)
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init submodules
run: git submodule update --init --recursive
submodules: recursive
- name: Set VERSION
run: |
@@ -450,13 +428,9 @@ jobs:
build-linux-daemon:
runs-on: ubuntu-22.04
steps:
- name: Checkout (with history for submodule)
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init submodules
run: git submodule update --init --recursive
submodules: recursive
- name: Set VERSION
run: |
@@ -586,13 +560,9 @@ jobs:
build-macos:
runs-on: macos-15
steps:
- name: Checkout (with history for submodule)
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init submodules
run: git submodule update --init --recursive
submodules: recursive
- name: Set VERSION
run: |
+1
View File
@@ -49,6 +49,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
- name: Install dependencies + clang-tidy
run: |
+1 -1
View File
@@ -6,7 +6,7 @@ if(POLICY CMP0167)
endif()
project(Triangles
VERSION 5.9.5
VERSION 6.0.0
DESCRIPTION "Cryptographic Triangles Wallet"
LANGUAGES C CXX
)
+7
View File
@@ -0,0 +1,7 @@
# Krystie runner log
This file records autonomous-runner activity. Each entry is a doc-only
edit produced by the demo worker; once OpenClaw is wired in this log
will be replaced by real work.
- [2026-04-29T06:57:30Z] triangles_v5#1 — Smoke-test the Krystie loop runner
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 17 KiB

+4
View File
@@ -42,6 +42,7 @@ set(CORE_SOURCES
bootstrap.cpp
checkpoints.cpp
crypter.cpp
hdwallet.cpp
crypto_ecdh.cpp
crypto_ecdsa.cpp
db.cpp
@@ -62,6 +63,8 @@ set(CORE_SOURCES
pbkdf2.cpp
scrypt.cpp
smessage.cpp
syncmanager.cpp
chaindb_migrate.cpp
tor_embed_hooks.cpp
rest.cpp
trianglesrpc.cpp
@@ -304,6 +307,7 @@ if(BUILD_QT)
qt/trianglesunits.cpp
qt/qvaluecombobox.cpp
qt/askpassphrasedialog.cpp
qt/hdseeddialog.cpp
qt/notificator.cpp
qt/qtipcserver.cpp
qt/rpcconsole.cpp
+14 -17
View File
@@ -81,15 +81,14 @@ double CAddrInfo::GetChance(int64_t nNow) const
CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int *pnId)
{
std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
auto it = mapAddr.find(addr);
if (it == mapAddr.end())
return NULL;
return nullptr;
if (pnId)
*pnId = (*it).second;
std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
if (it2 != mapInfo.end())
return &(*it2).second;
return NULL;
*pnId = it->second;
if (auto it2 = mapInfo.find(it->second); it2 != mapInfo.end())
return &it2->second;
return nullptr;
}
CAddrInfo* CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId)
@@ -177,13 +176,13 @@ int CAddrMan::ShrinkNew(int nUBucket)
int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())};
int nI = 0;
int nOldest = -1;
for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
for (const auto& elem : vNew)
{
if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3])
{
assert(nOldest == -1 || mapInfo.count(*it) == 1);
if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime)
nOldest = *it;
assert(nOldest == -1 || mapInfo.count(elem) == 1);
if (nOldest == -1 || mapInfo[elem].nTime < mapInfo[nOldest].nTime)
nOldest = elem;
}
nI++;
}
@@ -440,10 +439,8 @@ int CAddrMan::Check_()
if (vRandom.size() != nTried + nNew) return -7;
for (std::map<int, CAddrInfo>::iterator it = mapInfo.begin(); it != mapInfo.end(); it++)
for (auto& [n, info] : mapInfo)
{
int n = (*it).first;
CAddrInfo &info = (*it).second;
if (info.fInTried)
{
@@ -467,10 +464,10 @@ int CAddrMan::Check_()
for (int n=0; n<vvTried.size(); n++)
{
std::vector<int> &vTried = vvTried[n];
for (std::vector<int>::iterator it = vTried.begin(); it != vTried.end(); it++)
for (const auto& elem : vTried)
{
if (!setTried.count(*it)) return -11;
setTried.erase(*it);
if (!setTried.count(elem)) return -11;
setTried.erase(elem);
}
}
+13 -13
View File
@@ -66,7 +66,7 @@ public:
if(it == histogram.end()) // Newly locked page
{
locker.Lock(reinterpret_cast<void*>(page), page_size);
histogram.insert(std::make_pair(page, 1));
histogram.insert({page, 1});
}
else // Page was already locked; increase counter
{
@@ -193,25 +193,25 @@ struct secure_allocator : public std::allocator<T>
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
secure_allocator() throw() {}
secure_allocator(const secure_allocator& a) throw() : base(a) {}
secure_allocator() noexcept {}
secure_allocator(const secure_allocator& a) noexcept : base(a) {}
template <typename U>
secure_allocator(const secure_allocator<U>& a) throw() : base(a) {}
~secure_allocator() throw() {}
secure_allocator(const secure_allocator<U>& a) noexcept : base(a) {}
~secure_allocator() noexcept {}
template<typename _Other> struct rebind
{ typedef secure_allocator<_Other> other; };
T* allocate(std::size_t n)
{
T* p = std::allocator<T>::allocate(n);
if (p != NULL)
if (p != nullptr)
LockedPageManager::instance.LockRange(p, sizeof(T) * n);
return p;
}
void deallocate(T* p, std::size_t n)
{
if (p != NULL)
if (p != nullptr)
{
memset(p, 0, sizeof(T) * n);
LockedPageManager::instance.UnlockRange(p, sizeof(T) * n);
@@ -237,24 +237,24 @@ struct zero_after_free_allocator : public std::allocator<T>
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
zero_after_free_allocator() throw() {}
zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {}
zero_after_free_allocator() noexcept {}
zero_after_free_allocator(const zero_after_free_allocator& a) noexcept : base(a) {}
template <typename U>
zero_after_free_allocator(const zero_after_free_allocator<U>& a) throw() : base(a) {}
~zero_after_free_allocator() throw() {}
zero_after_free_allocator(const zero_after_free_allocator<U>& a) noexcept : base(a) {}
~zero_after_free_allocator() noexcept {}
template<typename _Other> struct rebind
{ typedef zero_after_free_allocator<_Other> other; };
void deallocate(T* p, std::size_t n)
{
if (p != NULL)
if (p != nullptr)
memset(p, 0, sizeof(T) * n);
std::allocator<T>::deallocate(p, n);
}
};
// This is exactly like std::string, but with a custom allocator.
typedef std::basic_string<char, std::char_traits<char>, secure_allocator<char> > SecureString;
using SecureString = std::basic_string<char, std::char_traits<char>, secure_allocator<char>>;
static inline SecureString MakeSecureString(const std::string& value)
{
+22 -14
View File
@@ -11,6 +11,7 @@
#include "version.h"
#include <openssl/bn.h>
#include <openssl/opensslv.h>
#include <algorithm>
#include <stdexcept>
@@ -37,20 +38,20 @@ public:
CAutoBN_CTX()
{
pctx = BN_CTX_new();
if (pctx == NULL)
if (pctx == nullptr)
throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL");
}
~CAutoBN_CTX()
{
if (pctx != NULL)
if (pctx != nullptr)
BN_CTX_free(pctx);
}
operator BN_CTX*() { return pctx; }
BN_CTX& operator*() { return *pctx; }
BN_CTX** operator&() { return &pctx; }
bool operator!() { return (pctx == NULL); }
bool operator!() { return (pctx == nullptr); }
};
@@ -64,14 +65,14 @@ public:
CBigNum()
{
pbn = BN_new();
if (pbn == NULL)
if (pbn == nullptr)
throw bignum_error("CBigNum::CBigNum() : BN_new() returned NULL");
}
CBigNum(const CBigNum& b)
{
pbn = BN_new();
if (pbn == NULL)
if (pbn == nullptr)
throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_new() returned NULL");
if (!BN_copy(pbn, b.pbn))
{
@@ -89,7 +90,7 @@ public:
~CBigNum()
{
if (pbn != NULL)
if (pbn != nullptr)
BN_clear_free(pbn);
}
@@ -220,7 +221,7 @@ public:
uint64_t getuint64()
{
unsigned int nSize = BN_bn2mpi(pbn, NULL);
unsigned int nSize = BN_bn2mpi(pbn, nullptr);
if (nSize < 4)
return 0;
std::vector<unsigned char> vch(nSize);
@@ -290,7 +291,7 @@ public:
uint256 getuint256() const
{
unsigned int nSize = BN_bn2mpi(pbn, NULL);
unsigned int nSize = BN_bn2mpi(pbn, nullptr);
if (nSize < 4)
return 0;
std::vector<unsigned char> vch(nSize);
@@ -321,7 +322,7 @@ public:
std::vector<unsigned char> getvch() const
{
unsigned int nSize = BN_bn2mpi(pbn, NULL);
unsigned int nSize = BN_bn2mpi(pbn, nullptr);
if (nSize <= 4)
return std::vector<unsigned char>();
std::vector<unsigned char> vch(nSize);
@@ -345,7 +346,7 @@ public:
unsigned int GetCompact() const
{
unsigned int nSize = BN_bn2mpi(pbn, NULL);
unsigned int nSize = BN_bn2mpi(pbn, nullptr);
std::vector<unsigned char> vch(nSize);
nSize -= 4;
BN_bn2mpi(pbn, &vch[0]);
@@ -374,7 +375,7 @@ public:
psz++;
// hex string to bignum
static const signed char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 };
static constexpr signed char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 };
*this = 0;
while (isxdigit(*psz))
{
@@ -515,7 +516,7 @@ public:
*/
static CBigNum generatePrime(const unsigned int numBits, bool safe = false) {
CBigNum ret;
if(!BN_generate_prime_ex(ret.pbn, numBits, (safe == true), NULL, NULL, NULL))
if(!BN_generate_prime_ex(ret.pbn, numBits, (safe == true), nullptr, nullptr, nullptr))
throw bignum_error("CBigNum::generatePrime*= :BN_generate_prime_ex");
return ret;
}
@@ -541,7 +542,14 @@ public:
*/
bool isPrime(const int checks=BN_prime_checks) const {
CAutoBN_CTX pctx;
int ret = BN_is_prime_ex(pbn, checks, pctx, NULL);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
int ret = BN_is_prime_ex(pbn, checks, pctx, nullptr);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#pragma GCC diagnostic pop
#endif
if(ret < 0){
throw bignum_error("CBigNum::isPrime :BN_is_prime_ex");
}
@@ -706,7 +714,7 @@ inline const CBigNum operator/(const CBigNum& a, const CBigNum& b)
{
CAutoBN_CTX pctx;
CBigNum r;
if (!BN_div(r.pbn, NULL, a.pbn, b.pbn, pctx))
if (!BN_div(r.pbn, nullptr, a.pbn, b.pbn, pctx))
throw bignum_error("CBigNum::operator/ : BN_div failed");
return r;
}
+263
View File
@@ -0,0 +1,263 @@
// BIP39 English wordlist (2048 words, canonical). Auto-generated; do not edit.
#ifndef TRIANGLES_BIP39_ENGLISH_H
#define TRIANGLES_BIP39_ENGLISH_H
static const char* const BIP39_WORDLIST_EN[2048] = {
"abandon","ability","able","about","above","absent","absorb","abstract",
"absurd","abuse","access","accident","account","accuse","achieve","acid",
"acoustic","acquire","across","act","action","actor","actress","actual",
"adapt","add","addict","address","adjust","admit","adult","advance",
"advice","aerobic","affair","afford","afraid","again","age","agent",
"agree","ahead","aim","air","airport","aisle","alarm","album",
"alcohol","alert","alien","all","alley","allow","almost","alone",
"alpha","already","also","alter","always","amateur","amazing","among",
"amount","amused","analyst","anchor","ancient","anger","angle","angry",
"animal","ankle","announce","annual","another","answer","antenna","antique",
"anxiety","any","apart","apology","appear","apple","approve","april",
"arch","arctic","area","arena","argue","arm","armed","armor",
"army","around","arrange","arrest","arrive","arrow","art","artefact",
"artist","artwork","ask","aspect","assault","asset","assist","assume",
"asthma","athlete","atom","attack","attend","attitude","attract","auction",
"audit","august","aunt","author","auto","autumn","average","avocado",
"avoid","awake","aware","away","awesome","awful","awkward","axis",
"baby","bachelor","bacon","badge","bag","balance","balcony","ball",
"bamboo","banana","banner","bar","barely","bargain","barrel","base",
"basic","basket","battle","beach","bean","beauty","because","become",
"beef","before","begin","behave","behind","believe","below","belt",
"bench","benefit","best","betray","better","between","beyond","bicycle",
"bid","bike","bind","biology","bird","birth","bitter","black",
"blade","blame","blanket","blast","bleak","bless","blind","blood",
"blossom","blouse","blue","blur","blush","board","boat","body",
"boil","bomb","bone","bonus","book","boost","border","boring",
"borrow","boss","bottom","bounce","box","boy","bracket","brain",
"brand","brass","brave","bread","breeze","brick","bridge","brief",
"bright","bring","brisk","broccoli","broken","bronze","broom","brother",
"brown","brush","bubble","buddy","budget","buffalo","build","bulb",
"bulk","bullet","bundle","bunker","burden","burger","burst","bus",
"business","busy","butter","buyer","buzz","cabbage","cabin","cable",
"cactus","cage","cake","call","calm","camera","camp","can",
"canal","cancel","candy","cannon","canoe","canvas","canyon","capable",
"capital","captain","car","carbon","card","cargo","carpet","carry",
"cart","case","cash","casino","castle","casual","cat","catalog",
"catch","category","cattle","caught","cause","caution","cave","ceiling",
"celery","cement","census","century","cereal","certain","chair","chalk",
"champion","change","chaos","chapter","charge","chase","chat","cheap",
"check","cheese","chef","cherry","chest","chicken","chief","child",
"chimney","choice","choose","chronic","chuckle","chunk","churn","cigar",
"cinnamon","circle","citizen","city","civil","claim","clap","clarify",
"claw","clay","clean","clerk","clever","click","client","cliff",
"climb","clinic","clip","clock","clog","close","cloth","cloud",
"clown","club","clump","cluster","clutch","coach","coast","coconut",
"code","coffee","coil","coin","collect","color","column","combine",
"come","comfort","comic","common","company","concert","conduct","confirm",
"congress","connect","consider","control","convince","cook","cool","copper",
"copy","coral","core","corn","correct","cost","cotton","couch",
"country","couple","course","cousin","cover","coyote","crack","cradle",
"craft","cram","crane","crash","crater","crawl","crazy","cream",
"credit","creek","crew","cricket","crime","crisp","critic","crop",
"cross","crouch","crowd","crucial","cruel","cruise","crumble","crunch",
"crush","cry","crystal","cube","culture","cup","cupboard","curious",
"current","curtain","curve","cushion","custom","cute","cycle","dad",
"damage","damp","dance","danger","daring","dash","daughter","dawn",
"day","deal","debate","debris","decade","december","decide","decline",
"decorate","decrease","deer","defense","define","defy","degree","delay",
"deliver","demand","demise","denial","dentist","deny","depart","depend",
"deposit","depth","deputy","derive","describe","desert","design","desk",
"despair","destroy","detail","detect","develop","device","devote","diagram",
"dial","diamond","diary","dice","diesel","diet","differ","digital",
"dignity","dilemma","dinner","dinosaur","direct","dirt","disagree","discover",
"disease","dish","dismiss","disorder","display","distance","divert","divide",
"divorce","dizzy","doctor","document","dog","doll","dolphin","domain",
"donate","donkey","donor","door","dose","double","dove","draft",
"dragon","drama","drastic","draw","dream","dress","drift","drill",
"drink","drip","drive","drop","drum","dry","duck","dumb",
"dune","during","dust","dutch","duty","dwarf","dynamic","eager",
"eagle","early","earn","earth","easily","east","easy","echo",
"ecology","economy","edge","edit","educate","effort","egg","eight",
"either","elbow","elder","electric","elegant","element","elephant","elevator",
"elite","else","embark","embody","embrace","emerge","emotion","employ",
"empower","empty","enable","enact","end","endless","endorse","enemy",
"energy","enforce","engage","engine","enhance","enjoy","enlist","enough",
"enrich","enroll","ensure","enter","entire","entry","envelope","episode",
"equal","equip","era","erase","erode","erosion","error","erupt",
"escape","essay","essence","estate","eternal","ethics","evidence","evil",
"evoke","evolve","exact","example","excess","exchange","excite","exclude",
"excuse","execute","exercise","exhaust","exhibit","exile","exist","exit",
"exotic","expand","expect","expire","explain","expose","express","extend",
"extra","eye","eyebrow","fabric","face","faculty","fade","faint",
"faith","fall","false","fame","family","famous","fan","fancy",
"fantasy","farm","fashion","fat","fatal","father","fatigue","fault",
"favorite","feature","february","federal","fee","feed","feel","female",
"fence","festival","fetch","fever","few","fiber","fiction","field",
"figure","file","film","filter","final","find","fine","finger",
"finish","fire","firm","first","fiscal","fish","fit","fitness",
"fix","flag","flame","flash","flat","flavor","flee","flight",
"flip","float","flock","floor","flower","fluid","flush","fly",
"foam","focus","fog","foil","fold","follow","food","foot",
"force","forest","forget","fork","fortune","forum","forward","fossil",
"foster","found","fox","fragile","frame","frequent","fresh","friend",
"fringe","frog","front","frost","frown","frozen","fruit","fuel",
"fun","funny","furnace","fury","future","gadget","gain","galaxy",
"gallery","game","gap","garage","garbage","garden","garlic","garment",
"gas","gasp","gate","gather","gauge","gaze","general","genius",
"genre","gentle","genuine","gesture","ghost","giant","gift","giggle",
"ginger","giraffe","girl","give","glad","glance","glare","glass",
"glide","glimpse","globe","gloom","glory","glove","glow","glue",
"goat","goddess","gold","good","goose","gorilla","gospel","gossip",
"govern","gown","grab","grace","grain","grant","grape","grass",
"gravity","great","green","grid","grief","grit","grocery","group",
"grow","grunt","guard","guess","guide","guilt","guitar","gun",
"gym","habit","hair","half","hammer","hamster","hand","happy",
"harbor","hard","harsh","harvest","hat","have","hawk","hazard",
"head","health","heart","heavy","hedgehog","height","hello","helmet",
"help","hen","hero","hidden","high","hill","hint","hip",
"hire","history","hobby","hockey","hold","hole","holiday","hollow",
"home","honey","hood","hope","horn","horror","horse","hospital",
"host","hotel","hour","hover","hub","huge","human","humble",
"humor","hundred","hungry","hunt","hurdle","hurry","hurt","husband",
"hybrid","ice","icon","idea","identify","idle","ignore","ill",
"illegal","illness","image","imitate","immense","immune","impact","impose",
"improve","impulse","inch","include","income","increase","index","indicate",
"indoor","industry","infant","inflict","inform","inhale","inherit","initial",
"inject","injury","inmate","inner","innocent","input","inquiry","insane",
"insect","inside","inspire","install","intact","interest","into","invest",
"invite","involve","iron","island","isolate","issue","item","ivory",
"jacket","jaguar","jar","jazz","jealous","jeans","jelly","jewel",
"job","join","joke","journey","joy","judge","juice","jump",
"jungle","junior","junk","just","kangaroo","keen","keep","ketchup",
"key","kick","kid","kidney","kind","kingdom","kiss","kit",
"kitchen","kite","kitten","kiwi","knee","knife","knock","know",
"lab","label","labor","ladder","lady","lake","lamp","language",
"laptop","large","later","latin","laugh","laundry","lava","law",
"lawn","lawsuit","layer","lazy","leader","leaf","learn","leave",
"lecture","left","leg","legal","legend","leisure","lemon","lend",
"length","lens","leopard","lesson","letter","level","liar","liberty",
"library","license","life","lift","light","like","limb","limit",
"link","lion","liquid","list","little","live","lizard","load",
"loan","lobster","local","lock","logic","lonely","long","loop",
"lottery","loud","lounge","love","loyal","lucky","luggage","lumber",
"lunar","lunch","luxury","lyrics","machine","mad","magic","magnet",
"maid","mail","main","major","make","mammal","man","manage",
"mandate","mango","mansion","manual","maple","marble","march","margin",
"marine","market","marriage","mask","mass","master","match","material",
"math","matrix","matter","maximum","maze","meadow","mean","measure",
"meat","mechanic","medal","media","melody","melt","member","memory",
"mention","menu","mercy","merge","merit","merry","mesh","message",
"metal","method","middle","midnight","milk","million","mimic","mind",
"minimum","minor","minute","miracle","mirror","misery","miss","mistake",
"mix","mixed","mixture","mobile","model","modify","mom","moment",
"monitor","monkey","monster","month","moon","moral","more","morning",
"mosquito","mother","motion","motor","mountain","mouse","move","movie",
"much","muffin","mule","multiply","muscle","museum","mushroom","music",
"must","mutual","myself","mystery","myth","naive","name","napkin",
"narrow","nasty","nation","nature","near","neck","need","negative",
"neglect","neither","nephew","nerve","nest","net","network","neutral",
"never","news","next","nice","night","noble","noise","nominee",
"noodle","normal","north","nose","notable","note","nothing","notice",
"novel","now","nuclear","number","nurse","nut","oak","obey",
"object","oblige","obscure","observe","obtain","obvious","occur","ocean",
"october","odor","off","offer","office","often","oil","okay",
"old","olive","olympic","omit","once","one","onion","online",
"only","open","opera","opinion","oppose","option","orange","orbit",
"orchard","order","ordinary","organ","orient","original","orphan","ostrich",
"other","outdoor","outer","output","outside","oval","oven","over",
"own","owner","oxygen","oyster","ozone","pact","paddle","page",
"pair","palace","palm","panda","panel","panic","panther","paper",
"parade","parent","park","parrot","party","pass","patch","path",
"patient","patrol","pattern","pause","pave","payment","peace","peanut",
"pear","peasant","pelican","pen","penalty","pencil","people","pepper",
"perfect","permit","person","pet","phone","photo","phrase","physical",
"piano","picnic","picture","piece","pig","pigeon","pill","pilot",
"pink","pioneer","pipe","pistol","pitch","pizza","place","planet",
"plastic","plate","play","please","pledge","pluck","plug","plunge",
"poem","poet","point","polar","pole","police","pond","pony",
"pool","popular","portion","position","possible","post","potato","pottery",
"poverty","powder","power","practice","praise","predict","prefer","prepare",
"present","pretty","prevent","price","pride","primary","print","priority",
"prison","private","prize","problem","process","produce","profit","program",
"project","promote","proof","property","prosper","protect","proud","provide",
"public","pudding","pull","pulp","pulse","pumpkin","punch","pupil",
"puppy","purchase","purity","purpose","purse","push","put","puzzle",
"pyramid","quality","quantum","quarter","question","quick","quit","quiz",
"quote","rabbit","raccoon","race","rack","radar","radio","rail",
"rain","raise","rally","ramp","ranch","random","range","rapid",
"rare","rate","rather","raven","raw","razor","ready","real",
"reason","rebel","rebuild","recall","receive","recipe","record","recycle",
"reduce","reflect","reform","refuse","region","regret","regular","reject",
"relax","release","relief","rely","remain","remember","remind","remove",
"render","renew","rent","reopen","repair","repeat","replace","report",
"require","rescue","resemble","resist","resource","response","result","retire",
"retreat","return","reunion","reveal","review","reward","rhythm","rib",
"ribbon","rice","rich","ride","ridge","rifle","right","rigid",
"ring","riot","ripple","risk","ritual","rival","river","road",
"roast","robot","robust","rocket","romance","roof","rookie","room",
"rose","rotate","rough","round","route","royal","rubber","rude",
"rug","rule","run","runway","rural","sad","saddle","sadness",
"safe","sail","salad","salmon","salon","salt","salute","same",
"sample","sand","satisfy","satoshi","sauce","sausage","save","say",
"scale","scan","scare","scatter","scene","scheme","school","science",
"scissors","scorpion","scout","scrap","screen","script","scrub","sea",
"search","season","seat","second","secret","section","security","seed",
"seek","segment","select","sell","seminar","senior","sense","sentence",
"series","service","session","settle","setup","seven","shadow","shaft",
"shallow","share","shed","shell","sheriff","shield","shift","shine",
"ship","shiver","shock","shoe","shoot","shop","short","shoulder",
"shove","shrimp","shrug","shuffle","shy","sibling","sick","side",
"siege","sight","sign","silent","silk","silly","silver","similar",
"simple","since","sing","siren","sister","situate","six","size",
"skate","sketch","ski","skill","skin","skirt","skull","slab",
"slam","sleep","slender","slice","slide","slight","slim","slogan",
"slot","slow","slush","small","smart","smile","smoke","smooth",
"snack","snake","snap","sniff","snow","soap","soccer","social",
"sock","soda","soft","solar","soldier","solid","solution","solve",
"someone","song","soon","sorry","sort","soul","sound","soup",
"source","south","space","spare","spatial","spawn","speak","special",
"speed","spell","spend","sphere","spice","spider","spike","spin",
"spirit","split","spoil","sponsor","spoon","sport","spot","spray",
"spread","spring","spy","square","squeeze","squirrel","stable","stadium",
"staff","stage","stairs","stamp","stand","start","state","stay",
"steak","steel","stem","step","stereo","stick","still","sting",
"stock","stomach","stone","stool","story","stove","strategy","street",
"strike","strong","struggle","student","stuff","stumble","style","subject",
"submit","subway","success","such","sudden","suffer","sugar","suggest",
"suit","summer","sun","sunny","sunset","super","supply","supreme",
"sure","surface","surge","surprise","surround","survey","suspect","sustain",
"swallow","swamp","swap","swarm","swear","sweet","swift","swim",
"swing","switch","sword","symbol","symptom","syrup","system","table",
"tackle","tag","tail","talent","talk","tank","tape","target",
"task","taste","tattoo","taxi","teach","team","tell","ten",
"tenant","tennis","tent","term","test","text","thank","that",
"theme","then","theory","there","they","thing","this","thought",
"three","thrive","throw","thumb","thunder","ticket","tide","tiger",
"tilt","timber","time","tiny","tip","tired","tissue","title",
"toast","tobacco","today","toddler","toe","together","toilet","token",
"tomato","tomorrow","tone","tongue","tonight","tool","tooth","top",
"topic","topple","torch","tornado","tortoise","toss","total","tourist",
"toward","tower","town","toy","track","trade","traffic","tragic",
"train","transfer","trap","trash","travel","tray","treat","tree",
"trend","trial","tribe","trick","trigger","trim","trip","trophy",
"trouble","truck","true","truly","trumpet","trust","truth","try",
"tube","tuition","tumble","tuna","tunnel","turkey","turn","turtle",
"twelve","twenty","twice","twin","twist","two","type","typical",
"ugly","umbrella","unable","unaware","uncle","uncover","under","undo",
"unfair","unfold","unhappy","uniform","unique","unit","universe","unknown",
"unlock","until","unusual","unveil","update","upgrade","uphold","upon",
"upper","upset","urban","urge","usage","use","used","useful",
"useless","usual","utility","vacant","vacuum","vague","valid","valley",
"valve","van","vanish","vapor","various","vast","vault","vehicle",
"velvet","vendor","venture","venue","verb","verify","version","very",
"vessel","veteran","viable","vibrant","vicious","victory","video","view",
"village","vintage","violin","virtual","virus","visa","visit","visual",
"vital","vivid","vocal","voice","void","volcano","volume","vote",
"voyage","wage","wagon","wait","walk","wall","walnut","want",
"warfare","warm","warrior","wash","wasp","waste","water","wave",
"way","wealth","weapon","wear","weasel","weather","web","wedding",
"weekend","weird","welcome","west","wet","whale","what","wheat",
"wheel","when","where","whip","whisper","wide","width","wife",
"wild","will","win","window","wine","wing","wink","winner",
"winter","wire","wisdom","wise","wish","witness","wolf","woman",
"wonder","wood","wool","word","work","world","worry","worth",
"wrap","wreck","wrestle","wrist","write","wrong","yard","year",
"yellow","you","young","youth","zebra","zero","zone","zoo",
};
#endif
+6 -7
View File
@@ -7,7 +7,6 @@
#include <filesystem>
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <zlib.h>
@@ -64,7 +63,7 @@ static SOCKET ConnectDirectTCP(const std::string& host, int port, std::string& s
}
SOCKET hSocket = INVALID_SOCKET;
for (rp = result; rp != NULL; rp = rp->ai_next) {
for (rp = result; rp != nullptr; rp = rp->ai_next) {
hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (hSocket == INVALID_SOCKET)
continue;
@@ -300,7 +299,7 @@ bool DownloadFile(const std::string& host, const std::string& urlPath,
location = headerData.substr(valStart, lineEnd - valStart);
else
location = headerData.substr(valStart);
boost::trim(location);
location = TrimString(location);
// Parse redirect URL — supports http://, https://, and relative paths
if (location.compare(0, 7, "http://") == 0 ||
@@ -416,7 +415,7 @@ bool FetchFileList(const std::string& host,
files.clear();
std::string line;
while (std::getline(in, line)) {
boost::trim(line);
line = TrimString(line);
if (!line.empty() && line[0] != '#')
files.push_back(line);
}
@@ -576,7 +575,7 @@ bool ParseManifest(const fs::path& manifestPath,
std::string line;
while (std::getline(in, line)) {
boost::trim(line);
line = TrimString(line);
if (line.empty() || line[0] == '#')
continue;
@@ -586,8 +585,8 @@ bool ParseManifest(const fs::path& manifestPath,
std::string key = line.substr(0, eq);
std::string val = line.substr(eq + 1);
boost::trim(key);
boost::trim(val);
key = TrimString(key);
val = TrimString(val);
if (key == "format")
manifest.format = std::atoi(val.c_str());
+198
View File
@@ -0,0 +1,198 @@
// Copyright (c) 2026 The Triangles developers.
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chaindb_migrate.h"
#include "txdb-leveldb.h"
#include "txdb-rocksdb.h"
#include "util.h"
#include <filesystem>
#include <fstream>
#include <memory>
namespace fs = std::filesystem;
namespace {
struct ChainDbStats
{
int64_t nRecords = 0;
int64_t nUtxos = 0;
int64_t nUtxoValue = 0;
uint256 hashBestChain = 0;
int nDbFormat = 0;
};
bool CollectStats(CTxDBBase& db, ChainDbStats& stats, std::string& strError)
{
stats = ChainDbStats();
auto it = db.NewIterator();
for (it->Seek(std::string()); it->Valid(); it->Next())
stats.nRecords++;
int nUtxos = 0;
stats.nUtxoValue = db.SumUtxoValues(nUtxos);
stats.nUtxos = nUtxos;
db.ReadHashBestChain(stats.hashBestChain);
db.ReadDbFormat(stats.nDbFormat);
if (stats.nRecords <= 0) {
strError = "source chain database contains no records";
return false;
}
return true;
}
bool StatsMatch(const ChainDbStats& src, const ChainDbStats& dst, std::string& strError)
{
if (src.nRecords != dst.nRecords) {
strError = strprintf("record count mismatch after migration: source=%lld rocksdb=%lld",
(long long)src.nRecords, (long long)dst.nRecords);
return false;
}
if (src.nUtxos != dst.nUtxos || src.nUtxoValue != dst.nUtxoValue) {
strError = strprintf("UTXO mismatch after migration: source=(%lld,%lld) rocksdb=(%lld,%lld)",
(long long)src.nUtxos, (long long)src.nUtxoValue,
(long long)dst.nUtxos, (long long)dst.nUtxoValue);
return false;
}
if (src.hashBestChain != dst.hashBestChain) {
strError = strprintf("best-chain hash mismatch after migration: source=%s rocksdb=%s",
src.hashBestChain.ToString().c_str(),
dst.hashBestChain.ToString().c_str());
return false;
}
if (src.nDbFormat != dst.nDbFormat) {
strError = strprintf("dbformat mismatch after migration: source=%d rocksdb=%d",
src.nDbFormat, dst.nDbFormat);
return false;
}
return true;
}
} // namespace
bool MaybeMigrateLevelDbToRocksDb(bool fForce, std::string& strError)
{
strError.clear();
const fs::path dataDir = GetDataDir();
const fs::path levelPath = dataDir / "txleveldb";
const fs::path rocksPath = dataDir / "rocksdb";
const fs::path markerPath = rocksPath / "MIGRATION_INCOMPLETE";
if (!fs::exists(levelPath))
return true;
if (fs::exists(rocksPath)) {
if (fs::exists(markerPath)) {
printf("ChainDB migration: removing incomplete previous RocksDB migration\n");
fs::remove_all(rocksPath);
}
else if (!fForce)
return true;
else {
printf("ChainDB migration: removing existing RocksDB directory due to -migratechaindbforce\n");
fs::remove_all(rocksPath);
}
}
printf("ChainDB migration: copying LevelDB chain state to RocksDB...\n");
printf("ChainDB migration: source=%s destination=%s\n",
levelPath.string().c_str(), rocksPath.string().c_str());
try {
fs::create_directories(rocksPath);
{
std::ofstream marker(markerPath);
marker << "RocksDB migration in progress. Safe to delete this directory and retry.\n";
}
CTxDB source("r");
CRocksTxDB destination("c+");
ChainDbStats srcStats;
if (!CollectStats(source, srcStats, strError)) {
source.Close();
destination.Close();
return false;
}
if (!destination.TxnBegin()) {
strError = "failed to begin RocksDB migration batch";
source.Close();
destination.Close();
return false;
}
int64_t nCopied = 0;
auto it = source.NewIterator();
for (it->Seek(std::string()); it->Valid(); it->Next())
{
if (!destination.WriteRawRecordForMigration(it->KeyStr(), it->ValueStr())) {
destination.TxnAbort();
strError = "failed to write migrated record to RocksDB";
source.Close();
destination.Close();
return false;
}
if (++nCopied % 100000 == 0)
{
if (!destination.TxnCommit()) {
strError = "failed to commit RocksDB migration batch";
source.Close();
destination.Close();
return false;
}
printf("ChainDB migration: copied %lld / %lld records\n",
(long long)nCopied, (long long)srcStats.nRecords);
if (!destination.TxnBegin()) {
strError = "failed to begin RocksDB migration batch";
source.Close();
destination.Close();
return false;
}
}
}
if (!destination.TxnCommit()) {
strError = "failed to commit final RocksDB migration batch";
source.Close();
destination.Close();
return false;
}
ChainDbStats dstStats;
if (!CollectStats(destination, dstStats, strError)) {
source.Close();
destination.Close();
return false;
}
if (!StatsMatch(srcStats, dstStats, strError)) {
source.Close();
destination.Close();
return false;
}
printf("ChainDB migration: verified %lld records, %lld UTXOs, best=%s\n",
(long long)dstStats.nRecords,
(long long)dstStats.nUtxos,
dstStats.hashBestChain.ToString().substr(0,20).c_str());
source.Close();
destination.Close();
fs::remove(markerPath);
}
catch (std::exception& e) {
strError = e.what();
return false;
}
printf("ChainDB migration: complete. Legacy LevelDB was left untouched at %s\n",
levelPath.string().c_str());
return true;
}
+14
View File
@@ -0,0 +1,14 @@
// Copyright (c) 2026 The Triangles developers.
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef TRIANGLES_CHAINDB_MIGRATE_H
#define TRIANGLES_CHAINDB_MIGRATE_H
#include <string>
// Migrate legacy LevelDB chain state from <datadir>/txleveldb to RocksDB in
// <datadir>/rocksdb. The source is never modified. Returns true when migration
// succeeds or when there is nothing to migrate.
bool MaybeMigrateLevelDbToRocksDb(bool fForce, std::string& strError);
#endif // TRIANGLES_CHAINDB_MIGRATE_H
+12 -24
View File
@@ -32,14 +32,11 @@ namespace Checkpoints
{ 9002, uint256("0xa1e20fb1d44688b763690cf74d6aefe859e4cc32981f9e3f2b2ae9702bbcf249")},
{ 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")},
{ 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")},
{2000000, uint256("0xb0b02d4bb5ffa31f6f22fd042082ca0a085257af34dc2d4b31c3c8567b5574d5")},
{2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")},
{2190000, uint256("0x682baf783581468ba18f9967254a7f3944e8b8c4cc7101e7d99b68f4f9dd5271")},
{2200000, uint256("0x0a8d0442f031f1258120f713f34e45f4f9a625fb753558e27b89b32ad5a9a740")},
{2205000, uint256("0xf7aa893ec012181e321783d6a5487addf6997377908faa3e760c9054a4217d29")},
{2206000, uint256("0x780ae878f8b10b6cbd51ceb2c0799c90d3551fa916be62974375071b9e36581c")},
{2203594, uint256("0x5e016ae5d1f163c6679292b717a3db467a39d24b0a315182f4783caa79c722d8")},
{2207000, uint256("0x8836d67b0f08036c4a7c26ff0a29d4461a52b6d8f552165ad9c1abec2f3cadfd")},
// Recent finality pin (PoS era). Closes the long unchecked span from
// 17650 to the live tip so stale-bootstrap / low-trust forks below
// this height are rejected outright. Hash from the canonical chain.
{ 2205000, uint256("0x6bdd3c5e5a32e1dd9a70e705f1a28d1dd84929f89579bd2696d41bc87f39446f")},
{ 2206004, uint256("0xb34e8e6a7bb7f52167d81aaad4d26f87a876898fdd0fce860916fc1aaf9a2a46")},
};
// Published UTXO snapshot file SHA256, keyed by snapshot height.
@@ -51,7 +48,7 @@ namespace Checkpoints
// here. The corresponding (height, blockhash) must already exist in
// mapCheckpoints / mapCheckpointsTestnet.
static std::map<int, uint256> mapSnapshotHashes = {
{2203594, uint256("0x49b35dd01659975c4a31954f37174c6e2e8878dd0723ab306ccecd991c80f79a")},
{ 2206004, uint256("0x1419282dae817315ee1b955543f6248233fe5800f5e8488734a0ece5bd6781ea")},
};
static std::map<int, uint256> mapSnapshotHashesTestnet = {
@@ -70,15 +67,6 @@ namespace Checkpoints
{ 9002, uint256("0xa1e20fb1d44688b763690cf74d6aefe859e4cc32981f9e3f2b2ae9702bbcf249")},
{ 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")},
{ 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")},
{2000000, uint256("0xb0b02d4bb5ffa31f6f22fd042082ca0a085257af34dc2d4b31c3c8567b5574d5")},
{2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")},
{2190000, uint256("0x682baf783581468ba18f9967254a7f3944e8b8c4cc7101e7d99b68f4f9dd5271")},
{2200000, uint256("0x0a8d0442f031f1258120f713f34e45f4f9a625fb753558e27b89b32ad5a9a740")},
{2205000, uint256("0xf7aa893ec012181e321783d6a5487addf6997377908faa3e760c9054a4217d29")},
{2206000, uint256("0x780ae878f8b10b6cbd51ceb2c0799c90d3551fa916be62974375071b9e36581c")},
{2207000, uint256("0x8836d67b0f08036c4a7c26ff0a29d4461a52b6d8f552165ad9c1abec2f3cadfd")},
{2208000, uint256("0xe4a19e8a29fa7aae47f7563377af3e896fee18ed069a64e325b8c2c6c820a1be")},
{2209000, uint256("0x04c78a6fc863bed918a9364c58c64489943b2e85d84ddb1ac2fba584f390d5dc")},
};
bool CheckHardened(int nHeight, const uint256& hash)
@@ -132,7 +120,7 @@ namespace Checkpoints
if (t != mapBlockIndex.end())
return t->second;
}
return NULL;
return nullptr;
}
// triangles: synchronized checkpoint (centrally broadcasted)
@@ -151,7 +139,7 @@ namespace Checkpoints
error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
else
return mapBlockIndex[hashSyncCheckpoint];
return NULL;
return nullptr;
}
// triangles: only descendant of current sync-checkpoint is allowed
@@ -281,8 +269,8 @@ namespace Checkpoints
return false;
if (hashBlock == hashPendingCheckpoint)
return true;
if (mapOrphanBlocks.count(hashPendingCheckpoint)
&& hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint]))
if (mapOrphanBlocks.count(hashPendingCheckpoint)
&& hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint].get()))
return true;
return false;
}
@@ -371,7 +359,7 @@ namespace Checkpoints
if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
return error("SendSyncCheckpoint: Unable to sign checkpoint, check private key?");
if(!checkpoint.ProcessSyncCheckpoint(NULL))
if(!checkpoint.ProcessSyncCheckpoint(nullptr))
{
printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n");
return false;
@@ -432,7 +420,7 @@ bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
pfrom->PushGetBlocks(pindexBest, hashCheckpoint);
// ask directly as well in case rejected earlier by duplicate
// proof-of-stake because getblocks may not get it this time
pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint));
pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint].get()) : hashCheckpoint));
}
return false;
}
+2 -2
View File
@@ -72,10 +72,10 @@ private:
nIdle--;
lock.unlock();
for (unsigned int i = 0; i < vChecks.size(); i++)
for (auto& check : vChecks)
{
if (fOk)
fOk = vChecks[i]();
fOk = check();
}
vChecks.clear();
+1 -1
View File
@@ -8,7 +8,7 @@
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it
#define CLIENT_VERSION_MAJOR 5
#define CLIENT_VERSION_MINOR 9
#define CLIENT_VERSION_REVISION 7
#define CLIENT_VERSION_REVISION 17
#define CLIENT_VERSION_BUILD 0
// Converts the parameter X to a string after macro replacement on X has been performed.
+2 -2
View File
@@ -75,7 +75,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned
bool fOk = true;
if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV);
if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, chKey, chIV);
if (fOk) fOk = EVP_EncryptUpdate(ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen);
if (fOk) fOk = EVP_EncryptFinal_ex(ctx, (&vchCiphertext[0])+nCLen, &nFLen);
EVP_CIPHER_CTX_free(ctx);
@@ -102,7 +102,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM
bool fOk = true;
if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV);
if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, chKey, chIV);
if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen);
if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0])+nPLen, &nFLen);
EVP_CIPHER_CTX_free(ctx);
+1 -1
View File
@@ -80,7 +80,7 @@ public:
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
using CKeyingMaterial = std::vector<unsigned char, secure_allocator<unsigned char>>;
/** Encryption/decryption context with key information */
class CCrypter
+1 -1
View File
@@ -26,7 +26,7 @@ secp256k1_context* GetECDHContext()
}
// Hash function callback that returns the raw X coordinate of the shared
// point. Mirrors OpenSSL's ECDH_compute_key behaviour when the KDF is NULL.
// point. Mirrors OpenSSL's ECDH_compute_key behaviour when the KDF is nullptr.
int hash_xonly(unsigned char* output,
const unsigned char* x32,
const unsigned char* /*y32*/,
+19 -19
View File
@@ -133,7 +133,7 @@ void CDBEnv::MakeMock()
#ifdef DB_LOG_IN_MEMORY
dbenv.log_set_config(DB_LOG_IN_MEMORY, 1);
#endif
int ret = dbenv.open(NULL,
int ret = dbenv.open(nullptr,
DB_CREATE |
DB_INIT_LOCK |
DB_INIT_LOG |
@@ -155,10 +155,10 @@ CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDB
assert(mapFileUseCount.count(strFile) == 0);
Db db(&dbenv, 0);
int result = db.verify(strFile.c_str(), NULL, NULL, 0);
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
if (result == 0)
return VERIFY_OK;
else if (recoverFunc == NULL)
else if (recoverFunc == nullptr)
return RECOVER_FAIL;
// Try to recover:
@@ -178,7 +178,7 @@ bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
stringstream strDump;
Db db(&dbenv, 0);
int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
int result = db.verify(strFile.c_str(), nullptr, &strDump, flags);
if (result == DB_VERIFY_BAD)
{
printf("Error: Salvage found errors, all data may not be recoverable.\n");
@@ -231,10 +231,10 @@ void CDBEnv::CheckpointLSN(std::string strFile)
CDB::CDB(const char *pszFile, const char* pszMode) :
pdb(NULL), activeTxn(NULL)
pdb(nullptr), activeTxn(nullptr)
{
int ret;
if (pszFile == NULL)
if (pszFile == nullptr)
return;
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
@@ -251,7 +251,7 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
strFile = pszFile;
++bitdb.mapFileUseCount[strFile];
pdb = bitdb.mapDb[strFile];
if (pdb == NULL)
if (pdb == nullptr)
{
pdb = new Db(&bitdb.dbenv, 0);
@@ -264,8 +264,8 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
}
ret = pdb->open(NULL, // Txn pointer
fMockDb ? NULL : pszFile, // Filename
ret = pdb->open(nullptr, // Txn pointer
fMockDb ? nullptr : pszFile, // Filename
"main", // Logical db name
DB_BTREE, // Database type
nFlags, // Flags
@@ -274,7 +274,7 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
if (ret != 0)
{
delete pdb;
pdb = NULL;
pdb = nullptr;
--bitdb.mapFileUseCount[strFile];
strFile = "";
throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
@@ -307,8 +307,8 @@ void CDB::Close()
return;
if (activeTxn)
activeTxn->abort();
activeTxn = NULL;
pdb = NULL;
activeTxn = nullptr;
pdb = nullptr;
// Flush database activity from memory pool to disk log
unsigned int nMinutes = 0;
@@ -331,13 +331,13 @@ void CDBEnv::CloseDb(const string& strFile)
{
{
LOCK(cs_db);
if (mapDb[strFile] != NULL)
if (mapDb[strFile] != nullptr)
{
// Close the database handle
Db* pdb = mapDb[strFile];
pdb->close(0);
delete pdb;
mapDb[strFile] = NULL;
mapDb[strFile] = nullptr;
}
}
}
@@ -347,7 +347,7 @@ bool CDBEnv::RemoveDb(const string& strFile)
this->CloseDb(strFile);
LOCK(cs_db);
int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
int rc = dbenv.dbremove(nullptr, strFile.c_str(), nullptr, DB_AUTO_COMMIT);
return (rc == 0);
}
@@ -371,7 +371,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip)
CDB db(strFile.c_str(), "r");
Db* pdbCopy = new Db(&bitdb.dbenv, 0);
int ret = pdbCopy->open(NULL, // Txn pointer
int ret = pdbCopy->open(nullptr, // Txn pointer
strFileRes.c_str(), // Filename
"main", // Logical db name
DB_BTREE, // Database type
@@ -412,7 +412,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip)
}
Dbt datKey(&ssKey[0], ssKey.size());
Dbt datValue(&ssValue[0], ssValue.size());
int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
if (ret2 > 0)
fSuccess = false;
}
@@ -428,10 +428,10 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip)
if (fSuccess)
{
Db dbA(&bitdb.dbenv, 0);
if (dbA.remove(strFile.c_str(), NULL, 0))
if (dbA.remove(strFile.c_str(), nullptr, 0))
fSuccess = false;
Db dbB(&bitdb.dbenv, 0);
if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
fSuccess = false;
}
if (!fSuccess)
+12 -12
View File
@@ -84,10 +84,10 @@ public:
DbTxn *TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
{
DbTxn* ptxn = NULL;
int ret = dbenv.txn_begin(NULL, &ptxn, flags);
DbTxn* ptxn = nullptr;
int ret = dbenv.txn_begin(nullptr, &ptxn, flags);
if (!ptxn || ret != 0)
return NULL;
return nullptr;
return ptxn;
}
};
@@ -130,7 +130,7 @@ protected:
datValue.set_flags(DB_DBT_MALLOC);
int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
memset(datKey.get_data(), 0, datKey.get_size());
if (datValue.get_data() == NULL)
if (datValue.get_data() == nullptr)
return false;
// Unserialize value
@@ -222,11 +222,11 @@ protected:
Dbc* GetCursor()
{
if (!pdb)
return NULL;
Dbc* pcursor = NULL;
int ret = pdb->cursor(NULL, &pcursor, 0);
return nullptr;
Dbc* pcursor = nullptr;
int ret = pdb->cursor(nullptr, &pcursor, 0);
if (ret != 0)
return NULL;
return nullptr;
return pcursor;
}
@@ -250,7 +250,7 @@ protected:
int ret = pcursor->get(&datKey, &datValue, fFlags);
if (ret != 0)
return ret;
else if (datKey.get_data() == NULL || datValue.get_data() == NULL)
else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
return 99999;
// Convert to streams
@@ -286,7 +286,7 @@ public:
if (!pdb || !activeTxn)
return false;
int ret = activeTxn->commit(0);
activeTxn = NULL;
activeTxn = nullptr;
return (ret == 0);
}
@@ -295,7 +295,7 @@ public:
if (!pdb || !activeTxn)
return false;
int ret = activeTxn->abort();
activeTxn = NULL;
activeTxn = nullptr;
return (ret == 0);
}
@@ -310,7 +310,7 @@ public:
return Write(std::string("version"), nVersion);
}
bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL);
bool static Rewrite(const std::string& strFile, const char* pszSkip = nullptr);
};
+223
View File
@@ -0,0 +1,223 @@
// Copyright (c) 2026 The Triangles developers
// Distributed under the MIT/X11 software license.
#include "hdwallet.h"
#include "bip39_english.h"
#include <cstring>
#include <algorithm>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <secp256k1.h>
namespace hd {
// ---- secp256k1 context (self-contained; independent of crypto_ecdsa) ------
static secp256k1_context* HDContext()
{
static secp256k1_context* ctx = NULL;
if (!ctx)
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
return ctx;
}
static void HmacSha512(const unsigned char* key, size_t keylen,
const unsigned char* data, size_t datalen,
unsigned char out[64])
{
unsigned int len = 64;
HMAC(EVP_sha512(), key, (int)keylen, data, datalen, out, &len);
}
// Binary search the (lexicographically sorted) BIP39 English wordlist.
static int WordIndex(const std::string& w)
{
int lo = 0, hi = 2047;
while (lo <= hi) {
int mid = (lo + hi) / 2;
int c = w.compare(BIP39_WORDLIST_EN[mid]);
if (c == 0) return mid;
if (c < 0) hi = mid - 1; else lo = mid + 1;
}
return -1;
}
static std::vector<std::string> SplitWords(const std::string& s)
{
std::vector<std::string> out;
size_t i = 0, n = s.size();
while (i < n) {
while (i < n && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r')) i++;
size_t j = i;
while (j < n && !(s[j] == ' ' || s[j] == '\t' || s[j] == '\n' || s[j] == '\r')) j++;
if (j > i) out.push_back(s.substr(i, j - i));
i = j;
}
return out;
}
// ---- BIP39 ----------------------------------------------------------------
std::string GenerateMnemonic(int strengthBits)
{
if (strengthBits != 128 && strengthBits != 256) strengthBits = 256;
int entBytes = strengthBits / 8;
std::vector<unsigned char> ent(entBytes);
if (RAND_bytes(&ent[0], entBytes) != 1) return std::string();
// checksum = first (ENT/32) bits of SHA256(entropy)
unsigned char hash[32];
SHA256(&ent[0], entBytes, hash);
int csBits = strengthBits / 32;
// bit buffer = entropy || checksum bits
std::vector<unsigned char> bits = ent;
bits.push_back(hash[0]); // up to 8 checksum bits live in hash[0]
int totalBits = strengthBits + csBits;
int words = totalBits / 11;
std::string out;
for (int i = 0; i < words; i++) {
int idx = 0;
for (int b = 0; b < 11; b++) {
int bitpos = i * 11 + b;
int byte = bitpos / 8, off = 7 - (bitpos % 8);
int bit = (bits[byte] >> off) & 1;
idx = (idx << 1) | bit;
}
if (i) out += ' ';
out += BIP39_WORDLIST_EN[idx];
}
return out;
}
bool CheckMnemonic(const std::string& mnemonic)
{
std::vector<std::string> w = SplitWords(mnemonic);
size_t nw = w.size();
if (nw != 12 && nw != 15 && nw != 18 && nw != 21 && nw != 24) return false;
int totalBits = (int)nw * 11;
int csBits = totalBits / 33;
int entBits = totalBits - csBits;
if (entBits % 8 != 0) return false;
int entBytes = entBits / 8;
// unpack 11-bit indices into a bit buffer
std::vector<unsigned char> buf((totalBits + 7) / 8, 0);
for (size_t i = 0; i < nw; i++) {
int idx = WordIndex(w[i]);
if (idx < 0) return false;
for (int b = 0; b < 11; b++) {
int bit = (idx >> (10 - b)) & 1;
int bitpos = (int)i * 11 + b;
int byte = bitpos / 8, off = 7 - (bitpos % 8);
if (bit) buf[byte] |= (1 << off);
}
}
std::vector<unsigned char> ent(buf.begin(), buf.begin() + entBytes);
unsigned char hash[32];
SHA256(&ent[0], entBytes, hash);
// compare csBits checksum bits
for (int b = 0; b < csBits; b++) {
int bitpos = entBits + b;
int byte = bitpos / 8, off = 7 - (bitpos % 8);
int got = (buf[byte] >> off) & 1;
int want = (hash[b / 8] >> (7 - (b % 8))) & 1;
if (got != want) return false;
}
return true;
}
bool MnemonicToSeed(const std::string& mnemonic, const std::string& passphrase,
unsigned char seed64[64])
{
std::string salt = "mnemonic" + passphrase;
int rc = PKCS5_PBKDF2_HMAC(mnemonic.c_str(), (int)mnemonic.size(),
(const unsigned char*)salt.c_str(), (int)salt.size(),
2048, EVP_sha512(), 64, seed64);
return rc == 1;
}
// ---- BIP32 ----------------------------------------------------------------
bool MasterFromSeed(const unsigned char* seed, size_t seedlen, ExtKey& out)
{
unsigned char I[64];
HmacSha512((const unsigned char*)"Bitcoin seed", 12, seed, seedlen, I);
memcpy(out.key, I, 32);
memcpy(out.chaincode, I + 32, 32);
if (!secp256k1_ec_seckey_verify(HDContext(), out.key)) return false;
out.valid = true;
return true;
}
bool CKDpriv(const ExtKey& parent, uint32_t index, ExtKey& child)
{
if (!parent.valid) return false;
secp256k1_context* ctx = HDContext();
unsigned char data[37];
size_t dlen = 0;
if (index & HARDENED) {
data[0] = 0x00;
memcpy(data + 1, parent.key, 32);
dlen = 33;
} else {
// serP(point(parent.key)) = 33-byte compressed pubkey
secp256k1_pubkey pk;
if (!secp256k1_ec_pubkey_create(ctx, &pk, parent.key)) return false;
size_t plen = 33;
secp256k1_ec_pubkey_serialize(ctx, data, &plen, &pk, SECP256K1_EC_COMPRESSED);
dlen = 33;
}
data[dlen + 0] = (index >> 24) & 0xff;
data[dlen + 1] = (index >> 16) & 0xff;
data[dlen + 2] = (index >> 8) & 0xff;
data[dlen + 3] = index & 0xff;
dlen += 4;
unsigned char I[64];
HmacSha512(parent.chaincode, 32, data, dlen, I);
memcpy(child.key, parent.key, 32);
// child = (IL + parent) mod n ; rejects invalid (IL>=n or result 0)
if (!secp256k1_ec_seckey_tweak_add(ctx, child.key, I)) return false;
memcpy(child.chaincode, I + 32, 32);
child.valid = true;
return true;
}
bool DerivePath(const ExtKey& master, const std::vector<uint32_t>& path, ExtKey& out)
{
ExtKey cur = master;
for (size_t i = 0; i < path.size(); i++) {
ExtKey nxt;
if (!CKDpriv(cur, path[i], nxt)) return false;
cur = nxt;
}
out = cur;
return true;
}
bool DeriveTriangles(const std::string& mnemonic, const std::string& passphrase,
uint32_t account, uint32_t change, uint32_t index,
unsigned char privOut[32])
{
unsigned char seed[64];
if (!MnemonicToSeed(mnemonic, passphrase, seed)) return false;
ExtKey master;
if (!MasterFromSeed(seed, 64, master)) return false;
std::vector<uint32_t> path;
path.push_back(44u | HARDENED);
path.push_back(TRI_COIN_TYPE | HARDENED);
path.push_back(account | HARDENED);
path.push_back(change);
path.push_back(index);
ExtKey leaf;
if (!DerivePath(master, path, leaf)) return false;
memcpy(privOut, leaf.key, 32);
return true;
}
} // namespace hd
+50
View File
@@ -0,0 +1,50 @@
// Copyright (c) 2026 The Triangles developers
// Distributed under the MIT/X11 software license.
//
// Native BIP39 (mnemonic) + BIP32 (HD) key derivation for Triangles.
// Produces keys identical to the TRIdock web wallet (derivation path
// m/44'/2222'/0'/0/i, coin type 2222), so a 24-word phrase round-trips
// between the Qt/daemon wallet and the web wallet.
#ifndef TRIANGLES_HDWALLET_H
#define TRIANGLES_HDWALLET_H
#include <string>
#include <vector>
#include <cstdint>
#include <cstddef>
namespace hd {
static const uint32_t HARDENED = 0x80000000u;
static const uint32_t TRI_COIN_TYPE = 2222u; // matches triWallet.js
// A BIP32 extended private key (private scalar + chain code).
struct ExtKey {
unsigned char key[32];
unsigned char chaincode[32];
bool valid;
ExtKey() : valid(false) { }
};
// ---- BIP39 ----------------------------------------------------------------
// Generate a new mnemonic. strengthBits must be 128 (12 words) or 256 (24).
std::string GenerateMnemonic(int strengthBits = 256);
// Validate word membership + checksum.
bool CheckMnemonic(const std::string& mnemonic);
// PBKDF2-HMAC-SHA512(mnemonic, "mnemonic"+passphrase, 2048) -> 64-byte seed.
bool MnemonicToSeed(const std::string& mnemonic, const std::string& passphrase,
unsigned char seed64[64]);
// ---- BIP32 ----------------------------------------------------------------
bool MasterFromSeed(const unsigned char* seed, size_t seedlen, ExtKey& out);
bool CKDpriv(const ExtKey& parent, uint32_t index, ExtKey& child);
bool DerivePath(const ExtKey& master, const std::vector<uint32_t>& path, ExtKey& out);
// ---- High level -----------------------------------------------------------
// Derive the 32-byte private scalar for m/44'/coinType'/account'/change/index.
bool DeriveTriangles(const std::string& mnemonic, const std::string& passphrase,
uint32_t account, uint32_t change, uint32_t index,
unsigned char privOut[32]);
} // namespace hd
#endif // TRIANGLES_HDWALLET_H
+83 -43
View File
@@ -24,12 +24,13 @@
#endif
#include "notificationqueue.h"
#include "addressindex.h"
#include "chaindb_migrate.h"
#include <memory>
#include <thread>
#include <vector>
#include <filesystem>
#include <fstream>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <openssl/crypto.h>
#ifndef WIN32
@@ -51,20 +52,20 @@ using namespace std;
using namespace boost;
namespace fs = std::filesystem;
CWallet* pwalletMain;
std::unique_ptr<CWallet> pwalletMain;
CClientUIInterface uiInterface;
std::string strWalletFileName;
bool fConfChange;
bool fEnforceCanonical;
unsigned int nNodeLifespan;
unsigned int nDerivationMethodIndex;
//unsigned int nMinerSleep;
bool fUseFastIndex;
enum Checkpoints::CPMode CheckpointsMode;
static CCriticalSection cs_DeferredStartup;
static bool fDeferredStartupRunning = false;
static std::vector<std::thread>* pScriptCheckThreads = nullptr;
static std::unique_ptr<std::vector<std::thread>> pScriptCheckThreads;
static void ThreadScriptCheck()
{
@@ -109,7 +110,7 @@ fRequestShutdown = true;
uiInterface.QueueShutdown();
#else
// Without UI, Shutdown() can simply be started in a new thread
NewThread(Shutdown, NULL);
NewThread(Shutdown, nullptr);
#endif
}
@@ -178,7 +179,7 @@ void ThreadDeferredStartup(void* parg)
}
catch (...)
{
PrintExceptionContinue(NULL, "ThreadDeferredStartup()");
PrintExceptionContinue(nullptr, "ThreadDeferredStartup()");
}
{
@@ -235,11 +236,9 @@ void Shutdown(void* parg)
{
for (std::thread& t : *pScriptCheckThreads)
if (t.joinable()) t.join();
delete pScriptCheckThreads;
pScriptCheckThreads = nullptr;
pScriptCheckThreads.reset();
}
delete pScriptCheckQueue;
pScriptCheckQueue = NULL;
pScriptCheckQueue.reset();
}
// NOW safe to destroy Tor state - all threads have stopped
@@ -251,24 +250,24 @@ void Shutdown(void* parg)
{
pzmqNotifier->Shutdown();
delete pzmqNotifier;
pzmqNotifier = NULL;
pzmqNotifier = nullptr;
}
#endif
if (pNotificationQueue)
{
delete pNotificationQueue;
pNotificationQueue = NULL;
pNotificationQueue = nullptr;
}
// MakeChainDB()->Close();
bitdb.Flush(false);
bitdb.Flush(true);
fs::remove(GetPidFile());
UnregisterWallet(pwalletMain);
delete pwalletMain;
UnregisterWallet(pwalletMain.get());
pwalletMain.reset();
// DB is flushed and wallet saved - safe to force-exit if something hangs
NewThread(ExitTimeout, NULL);
NewThread(ExitTimeout, nullptr);
MilliSleep(50);
printf("Triangles exited\n\n");
fExit = true;
@@ -318,7 +317,7 @@ bool AppInit(int argc, char* argv[])
if (!fs::is_directory(GetDataDir(false)))
{
fprintf(stderr, "Error: Specified directory does not exist\n");
Shutdown(NULL);
Shutdown(nullptr);
}
ReadConfigFile(mapArgs, mapMultiArgs);
@@ -340,7 +339,7 @@ bool AppInit(int argc, char* argv[])
// Command-line RPC
for (int i = 1; i < argc; i++)
if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "Triangles:"))
if (!IsSwitchChar(argv[i][0]) && !std::equal(std::begin("Triangles:"), std::end("Triangles:") - 1, argv[i], [](char a, char b) { return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b)); }))
fCommandLine = true;
if (fCommandLine)
@@ -354,10 +353,10 @@ bool AppInit(int argc, char* argv[])
catch (std::exception& e) {
PrintException(&e, "AppInit()");
} catch (...) {
PrintException(NULL, "AppInit()");
PrintException(nullptr, "AppInit()");
}
if (!fRet)
Shutdown(NULL);
Shutdown(nullptr);
return fRet;
}
@@ -542,7 +541,7 @@ bool AppInit2()
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0));
#endif
#if _MSC_VER >= 1400
// Disable confusing "helpful" text message on abort, Ctrl-C
@@ -559,7 +558,7 @@ bool AppInit2()
#endif
typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD);
PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy");
if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE);
if (setProcDEPPol != nullptr) setProcDEPPol(PROCESS_DEP_ENABLE);
#endif
#ifndef WIN32
umask(077);
@@ -569,15 +568,15 @@ bool AppInit2()
sa.sa_handler = HandleSIGTERM;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
// Reopen debug.log on SIGHUP
struct sigaction sa_hup;
sa_hup.sa_handler = HandleSIGHUP;
sigemptyset(&sa_hup.sa_mask);
sa_hup.sa_flags = 0;
sigaction(SIGHUP, &sa_hup, NULL);
sigaction(SIGHUP, &sa_hup, nullptr);
#endif
// ********************************************************* Step 2: parameter interactions
@@ -587,7 +586,7 @@ bool AppInit2()
//nMinerSleep = GetArg("-minersleep", 500);
CheckpointsMode = Checkpoints::STRICT;
std::string strCpMode = GetArg("-cppolicy", "strict");
std::string strCpMode = GetArg(std::string_view{"-cppolicy"}, std::string_view{"strict"});
if(strCpMode == "strict")
CheckpointsMode = Checkpoints::STRICT;
@@ -706,8 +705,8 @@ bool AppInit2()
nScriptCheckThreads = 16;
if (nScriptCheckThreads > 1)
{
pScriptCheckQueue = new CCheckQueue<CScriptCheck>(32);
pScriptCheckThreads = new std::vector<std::thread>();
pScriptCheckQueue = std::make_unique<CCheckQueue<CScriptCheck>>(32);
pScriptCheckThreads = std::make_unique<std::vector<std::thread>>();
for (int i = 0; i < nScriptCheckThreads - 1; ++i)
pScriptCheckThreads->emplace_back(&ThreadScriptCheck);
printf("Script verification threads: %d workers + main thread\n", nScriptCheckThreads - 1);
@@ -729,7 +728,7 @@ bool AppInit2()
return InitError(_("Initialization sanity check failed. Triangles is shutting down."));
std::string strDataDir = GetDataDir().string();
std::string strWalletFileName = GetArg("-wallet", "wallet.dat");
std::string strWalletFileName = GetArg(std::string_view{"-wallet"}, std::string_view{"wallet.dat"});
// strWalletFileName must be a plain filename without a directory
if (strWalletFileName != fs::path(strWalletFileName).stem().string() + fs::path(strWalletFileName).extension().string())
@@ -913,7 +912,7 @@ bool AppInit2()
if (mapArgs.count("-checkpointkey")) // triangles: checkpoint master priv key
{
if (!Checkpoints::SetCheckpointPrivKey(GetArg("-checkpointkey", "")))
if (!Checkpoints::SetCheckpointPrivKey(GetArg(std::string_view{"-checkpointkey"}, std::string_view{""})))
InitError(_("Unable to sign checkpoint, wrong checkpointkey?\n"));
}
@@ -1036,6 +1035,16 @@ bool AppInit2()
}
}
// ********************************************************* Step 6d: optional LevelDB -> RocksDB chain DB migration
if (GetBoolArg("-migratechaindb", false) || GetBoolArg("-migratechaindbforce", false))
{
uiInterface.InitMessage(_("Migrating chain database to RocksDB..."));
std::string strMigrateError;
bool fForce = GetBoolArg("-migratechaindbforce", false);
if (!MaybeMigrateLevelDbToRocksDb(fForce, strMigrateError))
return InitError(strprintf(_("Chain DB migration failed: %s"), strMigrateError.c_str()));
}
// ********************************************************* Step 7: load blockchain
if (!bitdb.Open(GetDataDir()))
@@ -1072,6 +1081,37 @@ bool AppInit2()
if (!LoadBlockIndex())
return InitError(_("Error loading blkindex.dat"));
// triangles fix (pitfall #61): initialize pindexFinalized from the
// hardcoded checkpoint on startup, BEFORE the daemon opens any peer
// connections or processes any block messages.
//
// Without this, pindexFinalized stays NULL on a fresh restart even when
// we have 2.2M blocks on disk, because the auto-checkpoint code in
// ActivateBestChain() at main.cpp:2459 only sets it when
// !IsInitialBlockDownload(). If the chain tip is more than 24h stale
// (which happens on every restart with a synced chain), IsInitialBlockDownload()
// returns true and pindexFinalized never gets set.
//
// The downstream reorg guard at main.cpp:2198 short-circuits when
// pindexFinalized is NULL, which allowed a 3,755-block minority fork
// to overwrite a healthy 2,206,004-block chain on 2026-06-16. Loading
// the hardcoded checkpoint from checkpoints.cpp (block 2,205,000) on
// startup means the reorg guard is always active whenever the
// checkpointed block is in our local mapBlockIndex.
{
CBlockIndex* pCheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
if (pCheckpoint && pCheckpoint != pindexFinalized)
{
pindexFinalized = pCheckpoint;
printf("STARTUP-CHECKPOINT: pindexFinalized set to block %d (%s) from hardcoded checkpoint\n",
pindexFinalized->nHeight, pindexFinalized->GetBlockHash().ToString().substr(0,20).c_str());
}
else if (!pCheckpoint)
{
printf("STARTUP-CHECKPOINT: WARNING — hardcoded checkpoint not in local block index, pindexFinalized remains NULL\n");
}
}
// If the block index is empty but blk0001.dat exists (bootstrap download),
// fast-import: build the index directly from the block file without re-writing
// data. Batches LevelDB commits every 200K blocks for speed.
@@ -1154,7 +1194,7 @@ bool AppInit2()
printf("Loading wallet...\n");
nStart = GetTimeMillis();
bool fFirstRun = true;
pwalletMain = new CWallet(strWalletFileName);
pwalletMain = std::make_unique<CWallet>(strWalletFileName);
// Auto-backup wallet.dat before loading (protects against corruption during load/flush)
{
@@ -1198,9 +1238,9 @@ bool AppInit2()
int nMaxVersion = GetArg("-upgradewallet", 0);
if (nMaxVersion == 0) // the -upgradewallet without argument case
{
printf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
printf("Performing wallet upgrade to %i\n", static_cast<int>(WalletFeature::Latest));
nMaxVersion = CLIENT_VERSION;
pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
pwalletMain->SetMinVersion(WalletFeature::Latest); // permanently upgrade the wallet immediately
}
else
printf("Allowing wallet upgrade up to %i\n", nMaxVersion);
@@ -1226,7 +1266,7 @@ bool AppInit2()
printf(" wallet %15" PRId64 "ms\n", GetTimeMillis() - nStart);
StartupPerfLog("wallet_load", GetTimeMillis() - nStart, strprintf("firstrun=%d", fFirstRun));
RegisterWallet(pwalletMain);
RegisterWallet(pwalletMain.get());
CBlockIndex *pindexRescan = pindexBest;
if (GetBoolArg("-rescan"))
@@ -1425,7 +1465,7 @@ bool AppInit2()
// Launch background thread for Tor health monitoring and seeder maintenance
if (torStarted) {
if (!NewThread(ThreadTorMaintenance, NULL))
if (!NewThread(ThreadTorMaintenance, nullptr))
printf("Warning: ThreadTorMaintenance could not be started\n");
}
}
@@ -1493,11 +1533,11 @@ bool AppInit2()
printf("mapWallet.size() = %" PRIszu "\n", pwalletMain->mapWallet.size());
printf("mapAddressBook.size() = %" PRIszu "\n", pwalletMain->mapAddressBook.size());
if (!NewThread(StartNode, NULL))
if (!NewThread(StartNode, nullptr))
InitError(_("Error: could not start node"));
if (fServer)
NewThread(ThreadRPCServer, NULL);
NewThread(ThreadRPCServer, nullptr);
// ********************************************************* Step 11.6: P2P UTXO snapshot fetch
// If the chain is empty and snapshot mode is enabled (default), spawn a
@@ -1513,7 +1553,7 @@ bool AppInit2()
if (snapshotMode && needsSnapshot && !haveSnapshotFile &&
Checkpoints::GetBestSnapshotHeight() > 0)
{
NewThread(ThreadSnapshotFetch, NULL);
NewThread(ThreadSnapshotFetch, nullptr);
}
}
@@ -1521,21 +1561,21 @@ bool AppInit2()
LOCK(cs_DeferredStartup);
fDeferredStartupRunning = true;
}
if (!NewThread(ThreadDeferredStartup, NULL))
if (!NewThread(ThreadDeferredStartup, nullptr))
{
printf("Warning: deferred startup thread could not be started, running inline\n");
ThreadDeferredStartup(NULL);
ThreadDeferredStartup(nullptr);
}
StartupPerfLog("start_services", GetTimeMillis() - nStart);
// ********************************************************* Step 11.5: ZMQ notifications
#ifdef ENABLE_ZMQ
{
std::string zmqAddr = GetArg("-zmqpubhashblock", "");
std::string zmqAddr = GetArg(std::string_view{"-zmqpubhashblock"}, std::string_view{""});
if (zmqAddr.empty())
zmqAddr = GetArg("-zmqpubhashtx", "");
zmqAddr = GetArg(std::string_view{"-zmqpubhashtx"}, std::string_view{""});
if (zmqAddr.empty())
zmqAddr = GetArg("-zmqpub", "");
zmqAddr = GetArg(std::string_view{"-zmqpub"}, std::string_view{""});
if (!zmqAddr.empty())
{
pzmqNotifier = new CZMQPublishNotifier();
@@ -1543,7 +1583,7 @@ bool AppInit2()
{
printf("ZMQ: Failed to initialize publisher on %s\n", zmqAddr.c_str());
delete pzmqNotifier;
pzmqNotifier = NULL;
pzmqNotifier = nullptr;
}
}
}
+2 -1
View File
@@ -7,8 +7,9 @@
#include "wallet.h"
#include "tor_embed_hooks.h"
#include <memory>
extern CWallet* pwalletMain;
extern std::unique_ptr<CWallet> pwalletMain;
extern std::string strWalletFileName;
void StartShutdown();
bool ShutdownRequested();
+12 -2
View File
@@ -14,7 +14,7 @@ extern unsigned int nTargetSpacing;
// Set to 20-minute for production network
//unsigned int nModifierInterval = MODIFIER_INTERVAL;
typedef std::map<int, unsigned int> MapModifierCheckpoints;
using MapModifierCheckpoints = std::map<int, unsigned int>;
// Hard checkpoints of stake modifiers to ensure they are deterministic
static std::map<int, unsigned int> mapStakeModifierCheckpoints = {
@@ -36,8 +36,18 @@ int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd)
// comes back with massively amplified staking power and dominates blocks.
// The 7-day cap still allows generous accumulation while limiting abuse.
static const int64_t STAKE_AGE_SOFT_CAP = 7 * 24 * 60 * 60; // 7 days
// Activation gate: the soft cap shipped 2026-04-20 without a height/time
// gate, retroactively invalidating earlier blocks staked with long-aged
// coins (e.g. coins idle through the 2022-2026 freeze). Apply the cap
// only to stakes after the activation timestamp; historical stakes
// validate under the rules they were created with (uncapped age).
static const int64_t STAKE_AGE_SOFT_CAP_ACTIVATION = 1776000000; // 2026-04-12 ~13:20 UTC
if (pindexBest && pindexBest->nHeight >= FORK_HEIGHT_V5)
return min(nAge, STAKE_AGE_SOFT_CAP);
{
if (nIntervalEnd >= STAKE_AGE_SOFT_CAP_ACTIVATION)
return min(nAge, STAKE_AGE_SOFT_CAP);
return nAge;
}
return min(nAge, (int64_t)nStakeMaxAge);
}
+3 -4
View File
@@ -68,7 +68,7 @@ public:
CPubKey() { }
CPubKey(const std::vector<unsigned char> &vchPubKeyIn) : vchPubKey(vchPubKeyIn) { }
friend bool operator==(const CPubKey &a, const CPubKey &b) { return a.vchPubKey == b.vchPubKey; }
friend bool operator!=(const CPubKey &a, const CPubKey &b) { return a.vchPubKey != b.vchPubKey; }
friend bool operator!=(const CPubKey &a, const CPubKey &b) = default;
friend bool operator<(const CPubKey &a, const CPubKey &b) { return a.vchPubKey < b.vchPubKey; }
IMPLEMENT_SERIALIZE(
@@ -99,9 +99,8 @@ public:
// secure_allocator is defined in allocators.h
// CPrivKey is a serialized private key, with all parameters included (279 bytes)
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
// CSecret is a serialization of just the secret parameter (32 bytes)
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
using CPrivKey = std::vector<unsigned char, secure_allocator<unsigned char>>;
using CSecret = std::vector<unsigned char, secure_allocator<unsigned char>>;
/** An encapsulated secp256k1 elliptic-curve key (public and/or private). */
class CKey
+14 -18
View File
@@ -50,10 +50,9 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut)
{
{
LOCK(cs_KeyStore);
ScriptMap::const_iterator mi = mapScripts.find(hash);
if (mi != mapScripts.end())
if (auto mi = mapScripts.find(hash); mi != mapScripts.end())
{
redeemScriptOut = (*mi).second;
redeemScriptOut = mi->second;
return true;
}
}
@@ -94,20 +93,19 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
if (!SetCrypted())
return false;
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
for (; mi != mapCryptedKeys.end(); ++mi)
for (const auto& [pubKeyHash, val] : mapCryptedKeys)
{
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
const CPubKey &vchPubKey = val.first;
const std::vector<unsigned char> &vchCryptedSecret = val.second;
CSecret vchSecret;
if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
return false;
if (vchSecret.size() != 32)
return false;
CKey key;
key.SetPubKey(vchPubKey);
key.SetSecret(vchSecret);
if (key.GetPubKey() == vchPubKey)
CKey decryptedKey;
decryptedKey.SetPubKey(vchPubKey);
decryptedKey.SetSecret(vchSecret);
if (decryptedKey.GetPubKey() == vchPubKey)
break;
return false;
}
@@ -159,11 +157,10 @@ bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const
if (!IsCrypted())
return CBasicKeyStore::GetKey(address, keyOut);
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
if (mi != mapCryptedKeys.end())
if (auto mi = mapCryptedKeys.find(address); mi != mapCryptedKeys.end())
{
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
const CPubKey &vchPubKey = mi->second.first;
const std::vector<unsigned char> &vchCryptedSecret = mi->second.second;
CSecret vchSecret;
if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
return false;
@@ -184,10 +181,9 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co
if (!IsCrypted())
return CKeyStore::GetPubKey(address, vchPubKeyOut);
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
if (mi != mapCryptedKeys.end())
if (auto mi = mapCryptedKeys.find(address); mi != mapCryptedKeys.end())
{
vchPubKeyOut = (*mi).second.first;
vchPubKeyOut = mi->second.first;
return true;
}
}
+10 -14
View File
@@ -44,8 +44,8 @@ public:
}
};
typedef std::map<CKeyID, std::pair<CSecret, bool> > KeyMap;
typedef std::map<CScriptID, CScript > ScriptMap;
using KeyMap = std::map<CKeyID, std::pair<CSecret, bool>>;
using ScriptMap = std::map<CScriptID, CScript>;
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
@@ -70,11 +70,9 @@ public:
setAddress.clear();
{
LOCK(cs_KeyStore);
KeyMap::const_iterator mi = mapKeys.begin();
while (mi != mapKeys.end())
for (const auto& [key, val] : mapKeys)
{
setAddress.insert((*mi).first);
mi++;
setAddress.insert(key);
}
}
}
@@ -82,11 +80,10 @@ public:
{
{
LOCK(cs_KeyStore);
KeyMap::const_iterator mi = mapKeys.find(address);
if (mi != mapKeys.end())
if (auto mi = mapKeys.find(address); mi != mapKeys.end())
{
keyOut.Reset();
keyOut.SetSecret((*mi).second.first, (*mi).second.second);
keyOut.SetSecret(mi->second.first, mi->second.second);
return true;
}
}
@@ -97,7 +94,7 @@ public:
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const;
};
typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
/** Keystore which keeps the private keys encrypted.
* It derives from the basic key store, which is used if no encryption is active.
@@ -160,17 +157,16 @@ public:
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
void GetKeys(std::set<CKeyID> &setAddress) const
{
LOCK(cs_KeyStore);
if (!IsCrypted())
{
CBasicKeyStore::GetKeys(setAddress);
return;
}
setAddress.clear();
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
while (mi != mapCryptedKeys.end())
for (const auto& [key, val] : mapCryptedKeys)
{
setAddress.insert((*mi).first);
mi++;
setAddress.insert(key);
}
}
+292 -910
View File
File diff suppressed because it is too large Load Diff
+75 -103
View File
@@ -15,6 +15,8 @@
#include "sigcache.h"
#include <list>
#include <array>
#include <memory>
class CWallet;
class CBlock;
@@ -29,29 +31,29 @@ class CRequestTracker;
class CNode;
class CScriptCheck;
static const int CUTOFF_POW_BLOCK = 9000;
static const int CRAPCHAIN_CUTOFF_BLOCK = 17691; // pre-Pharao (version 4) blockchain until block 17691
static const int FORK_HEIGHT_V5 = 17651; // v5 hard fork: decentralization + Tor v3 (next block after last checkpoint)
static const int FORK_HEIGHT_V5_4 = 2186941; // v5.4: tighter timestamps, deterministic fork resolution
constexpr int CUTOFF_POW_BLOCK = 9000;
constexpr int CRAPCHAIN_CUTOFF_BLOCK = 17691; // pre-Pharao (version 4) blockchain until block 17691
constexpr int FORK_HEIGHT_V5 = 17651; // v5 hard fork: decentralization + Tor v3 (next block after last checkpoint)
constexpr int FORK_HEIGHT_V5_4 = 2186941; // v5.4: tighter timestamps, deterministic fork resolution
static const unsigned int MAX_BLOCK_SIZE = 1000000;
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
static const unsigned int MAX_ORPHAN_BLOCKS = 750;
static const unsigned int MAX_ORPHAN_BLOCKS_IBD = 1500;
static const unsigned int MAX_REORG_DEPTH = 100; // reject reorgs deeper than this (finality)
static const unsigned int MAX_INV_SZ = 50000;
static const int64_t MIN_TX_FEE = (1 * CENT) / 100;
static const int64_t MIN_RELAY_TX_FEE = (1 * CENT) / 100;
static const int64_t MAX_MONEY = 2222222 * COIN;
static const int64_t COIN_YEAR_REWARD = 33 * CENT; // 33% per year
static const int64_t MAX_TRI_PROOF_OF_STAKE = 0.33 * COIN;
static const int MODIFIER_INTERVAL_SWITCH = 1;
constexpr unsigned int MAX_BLOCK_SIZE = 1000000;
constexpr unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
constexpr unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
constexpr unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
constexpr unsigned int MAX_ORPHAN_BLOCKS = 750;
constexpr unsigned int MAX_ORPHAN_BLOCKS_IBD = 1500;
constexpr unsigned int MAX_REORG_DEPTH = 100; // reject reorgs deeper than this (finality)
constexpr unsigned int MAX_INV_SZ = 50000;
constexpr int64_t MIN_TX_FEE = (1 * CENT) / 100;
constexpr int64_t MIN_RELAY_TX_FEE = (1 * CENT) / 100;
constexpr int64_t MAX_MONEY = 2222222 * COIN;
constexpr int64_t COIN_YEAR_REWARD = 33 * CENT; // 33% per year
constexpr int64_t MAX_TRI_PROOF_OF_STAKE = 0.33 * COIN;
constexpr int MODIFIER_INTERVAL_SWITCH = 1;
inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
constexpr unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
#ifdef USE_UPNP
static const int fHaveUPnP = true;
@@ -95,7 +97,7 @@ extern int64_t nTimeBestReceived;
extern CCriticalSection cs_setpwalletRegistered;
extern std::set<CWallet*> setpwalletRegistered;
extern unsigned char pchMessageStart[4];
extern std::map<uint256, CBlock*> mapOrphanBlocks;
extern std::map<uint256, std::unique_ptr<CBlock>> mapOrphanBlocks;
// Settings
extern int64_t nTransactionFee;
@@ -107,7 +109,7 @@ extern unsigned int nDerivationMethodIndex;
extern bool fEnforceCanonical;
// Minimum disk space required - used in CheckDiskSpace()
static const uint64_t nMinDiskSpace = 52428800;
constexpr uint64_t nMinDiskSpace = 52428800;
class CReserveKey;
class CTxDBBase;
@@ -115,7 +117,7 @@ class CTxIndex;
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true);
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = nullptr, bool fUpdate = false, bool fConnect = true);
bool ProcessBlock(CNode* pfrom, CBlock* pblock);
bool CheckDiskSpace(uint64_t nAdditionalBytes=0);
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb");
@@ -135,7 +137,7 @@ int64_t GetProofOfStakeReward(int64_t nCoinAge, int64_t nFees);
unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime);
unsigned int ComputeMinStake(unsigned int nBase, int64_t nTime, unsigned int nBlockTime);
int GetNumBlocksOfPeers();
bool IsInitialBlockDownload();
[[nodiscard]] bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor);
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock);
uint256 WantedByOrphan(const CBlock* pblockOrphan);
@@ -186,10 +188,7 @@ public:
a.nTxPos == b.nTxPos);
}
friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b)
{
return !(a == b);
}
friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) = default;
std::string ToString() const
@@ -217,8 +216,8 @@ public:
CInPoint() { SetNull(); }
CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; }
void SetNull() { ptx = NULL; n = (unsigned int) -1; }
bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); }
void SetNull() { ptx = nullptr; n = (unsigned int) -1; }
bool IsNull() const { return (ptx == nullptr && n == (unsigned int) -1); }
};
@@ -246,10 +245,7 @@ public:
return (a.hash == b.hash && a.n == b.n);
}
friend bool operator!=(const COutPoint& a, const COutPoint& b)
{
return !(a == b);
}
friend bool operator!=(const COutPoint& a, const COutPoint& b) = default;
std::string ToString() const
{
@@ -314,10 +310,7 @@ public:
a.nSequence == b.nSequence);
}
friend bool operator!=(const CTxIn& a, const CTxIn& b)
{
return !(a == b);
}
friend bool operator!=(const CTxIn& a, const CTxIn& b) = default;
std::string ToStringShort() const
{
@@ -407,10 +400,7 @@ public:
a.scriptPubKey == b.scriptPubKey);
}
friend bool operator!=(const CTxOut& a, const CTxOut& b)
{
return !(a == b);
}
friend bool operator!=(const CTxOut& a, const CTxOut& b) = default;
std::string ToStringShort() const
{
@@ -434,11 +424,11 @@ public:
enum GetMinFee_mode
enum class GetMinFeeMode : int
{
GMF_BLOCK,
GMF_RELAY,
GMF_SEND,
Block,
Relay,
Send,
};
/** A single unspent transaction output entry in the UTXO database.
@@ -485,7 +475,7 @@ public:
}
};
typedef std::map<COutPoint, CUtxoEntry> MapPrevTx;
using MapPrevTx = std::map<COutPoint, CUtxoEntry>;
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
@@ -599,7 +589,7 @@ public:
/** Check for standard transaction types
@return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
bool IsStandard() const;
[[nodiscard]] bool IsStandard() const;
/** Check for standard transaction types
@param[in] mapInputs Map of previous transactions that have outputs we're spending
@@ -647,9 +637,9 @@ public:
*/
int64_t GetValueIn(const MapPrevTx& mapInputs) const;
int64_t GetMinFee(unsigned int nBlockSize=1, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0) const;
int64_t GetMinFee(unsigned int nBlockSize=1, GetMinFeeMode mode=GetMinFeeMode::Block, unsigned int nBytes = 0) const;
bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL)
bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=nullptr)
{
CAutoFile filein = CAutoFile(OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION);
if (!filein)
@@ -685,10 +675,7 @@ public:
a.nLockTime == b.nLockTime);
}
friend bool operator!=(const CTransaction& a, const CTransaction& b)
{
return !(a == b);
}
friend bool operator!=(const CTransaction& a, const CTransaction& b) = default;
std::string ToStringShort() const
{
@@ -708,10 +695,10 @@ public:
vin.size(),
vout.size(),
nLockTime);
for (unsigned int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n";
for (unsigned int i = 0; i < vout.size(); i++)
str += " " + vout[i].ToString() + "\n";
for (const CTxIn& txin : vin)
str += " " + txin.ToString() + "\n";
for (const CTxOut& txout : vout)
str += " " + txout.ToString() + "\n";
return str;
}
@@ -747,12 +734,12 @@ public:
@param[in] fMiner true if called from CreateNewBlock
@return Returns true if all checks succeed
*/
bool ConnectInputs(CTxDBBase& txdb, const MapPrevTx& inputs,
[[nodiscard]] bool ConnectInputs(CTxDBBase& txdb, const MapPrevTx& inputs,
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner,
std::vector<CScriptCheck>* pvChecks = NULL);
std::vector<CScriptCheck>* pvChecks = nullptr);
bool ClientConnectInputs();
bool CheckTransaction() const;
bool AcceptToMemoryPool(CTxDBBase& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
[[nodiscard]] bool CheckTransaction() const;
[[nodiscard]] bool AcceptToMemoryPool(CTxDBBase& txdb, bool fCheckInputs=true, bool* pfMissingInputs=nullptr);
bool GetCoinAge(CTxDBBase& txdb, uint64_t& nCoinAge) const; // triangles: get transaction coin age
protected:
@@ -805,7 +792,7 @@ public:
)
int SetMerkleBranch(const CBlock* pblock=NULL);
int SetMerkleBranch(const CBlock* pblock=nullptr);
// Return depth of transaction in blockchain:
// -1 : not in blockchain, and not in memory pool (conflicted transaction)
@@ -869,10 +856,7 @@ public:
a.vSpent == b.vSpent);
}
friend bool operator!=(const CTxIndex& a, const CTxIndex& b)
{
return !(a == b);
}
friend bool operator!=(const CTxIndex& a, const CTxIndex& b) = default;
int GetDepthInMainChain() const;
};
@@ -957,6 +941,7 @@ public:
vMerkleTree.clear();
nDoS = 0;
fCachedHash = false;
fMerkleTreeCached = false;
}
bool IsNull() const
@@ -966,6 +951,7 @@ public:
mutable uint256 cachedHash;
mutable bool fCachedHash;
mutable bool fMerkleTreeCached;
uint256 GetHash() const
{
@@ -1007,7 +993,7 @@ public:
std::pair<COutPoint, unsigned int> GetProofOfStake() const
{
return IsProofOfStake()? std::make_pair(vtx[1].vin[0].prevout, vtx[1].nTime) : std::make_pair(COutPoint(), (unsigned int)0);
return IsProofOfStake()? std::pair{vtx[1].vin[0].prevout, vtx[1].nTime} : std::pair{COutPoint(), (unsigned int)0};
}
// triangles: get max transaction timestamp
@@ -1021,6 +1007,9 @@ public:
uint256 BuildMerkleTree() const
{
if (fMerkleTreeCached)
return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
vMerkleTree.clear();
for (const CTransaction& tx : vtx)
vMerkleTree.push_back(tx.GetHash());
@@ -1035,6 +1024,7 @@ public:
}
j += nSize;
}
fMerkleTreeCached = true;
return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
}
@@ -1134,25 +1124,25 @@ public:
nTime, nBits, nNonce,
vtx.size(),
HexStr(vchBlockSig.begin(), vchBlockSig.end()).c_str());
for (unsigned int i = 0; i < vtx.size(); i++)
for (const CTransaction& tx : vtx)
{
printf(" ");
vtx[i].print();
tx.print();
}
printf(" vMerkleTree: ");
for (unsigned int i = 0; i < vMerkleTree.size(); i++)
printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str());
for (const uint256& merkle : vMerkleTree)
printf("%s ", merkle.ToString().substr(0,10).c_str());
printf("\n");
}
bool DisconnectBlock(CTxDBBase& txdb, CBlockIndex* pindex);
bool ConnectBlock(CTxDBBase& txdb, CBlockIndex* pindex, bool fJustCheck=false);
[[nodiscard]] bool ConnectBlock(CTxDBBase& txdb, CBlockIndex* pindex, bool fJustCheck=false);
bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
bool SetBestChain(CTxDBBase& txdb, CBlockIndex* pindexNew);
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const uint256& hashProofOfStake);
bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const;
bool AcceptBlock();
[[nodiscard]] bool AcceptBlock();
bool GetCoinAge(uint64_t& nCoinAge) const; // triangles: calculate total coin age spent in block
bool SignBlock(CWallet& keystore, int64_t nFees);
bool CheckBlockSignature() const;
@@ -1212,9 +1202,9 @@ public:
CBlockIndex()
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
phashBlock = nullptr;
pprev = nullptr;
pnext = nullptr;
nFile = 0;
nBlockPos = 0;
nHeight = 0;
@@ -1235,32 +1225,16 @@ public:
nNonce = 0;
}
CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block)
CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) : CBlockIndex()
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nFile = nFileIn;
nBlockPos = nBlockPosIn;
nHeight = 0;
nChainTrust = 0;
nMint = 0;
nMoneySupply = 0;
nFlags = 0;
nStakeModifier = 0;
nStakeModifierChecksum = 0;
hashProofOfStake = 0;
if (block.IsProofOfStake())
{
SetProofOfStake();
prevoutStake = block.vtx[1].vin[0].prevout;
nStakeTime = block.vtx[1].nTime;
}
else
{
prevoutStake.SetNull();
nStakeTime = 0;
}
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
@@ -1313,9 +1287,9 @@ public:
int64_t GetMedianTimePast() const
{
int64_t pmedian[nMedianTimeSpan];
int64_t* pbegin = &pmedian[nMedianTimeSpan];
int64_t* pend = &pmedian[nMedianTimeSpan];
std::array<int64_t, nMedianTimeSpan> pmedian{};
auto pbegin = pmedian.end();
auto pend = pmedian.end();
const CBlockIndex* pindex = this;
for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev)
@@ -1541,10 +1515,7 @@ public:
Set((*mi).second);
}
CBlockLocator(const std::vector<uint256>& vHaveIn)
{
vHave = vHaveIn;
}
CBlockLocator(std::vector<uint256> vHaveIn) : vHave(std::move(vHaveIn)) {}
IMPLEMENT_SERIALIZE
(
@@ -1678,6 +1649,7 @@ public:
bool exists(uint256 hash)
{
LOCK(cs);
return (mapTx.count(hash) != 0);
}
@@ -1744,7 +1716,7 @@ public:
{
if (i <= 1) {
// Always prefill coinbase (idx 0) and coinstake (idx 1)
vPrefilledTxn.push_back(std::make_pair(i, block.vtx[i]));
vPrefilledTxn.push_back({i, block.vtx[i]});
} else {
vShortTxIds.push_back(GetShortTxId(block.vtx[i].GetHash(), nShortIdNonce));
}
@@ -1818,7 +1790,7 @@ private:
int nHashType;
public:
CScriptCheck() : ptxTo(NULL), nIn(0), nHashType(0) {}
CScriptCheck() : ptxTo(nullptr), nIn(0), nHashType(0) {}
CScriptCheck(const CScript& scriptPubKeyIn, const CScript& scriptSigIn,
const CTransaction& txToIn, unsigned int nInIn, int nHashTypeIn)
@@ -1845,6 +1817,6 @@ public:
}
};
extern CCheckQueue<CScriptCheck>* pScriptCheckQueue;
extern std::unique_ptr<CCheckQueue<CScriptCheck>> pScriptCheckQueue;
#endif
+7 -8
View File
@@ -50,7 +50,7 @@ uint64_t nLastBlockSize = 0;
int64_t nLastCoinStakeSearchInterval = 0;
// We want to sort transactions by priority and fee, so:
typedef std::tuple<double, double, CTransaction*> TxPriority;
using TxPriority = std::tuple<double, double, CTransaction*>;
class TxPriorityCompare
{
bool byFee;
@@ -79,7 +79,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
// Create new block
unique_ptr<CBlock> pblock(new CBlock());
if (!pblock.get())
return NULL;
return nullptr;
CBlockIndex* pindexPrev = pindexBest;
@@ -145,13 +145,12 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
// This vector will be sorted into a priority queue:
vector<TxPriority> vecPriority;
vecPriority.reserve(mempool.mapTx.size());
for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
for (auto& [hash, tx] : mempool.mapTx)
{
CTransaction& tx = (*mi).second;
if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal())
continue;
COrphan* porphan = NULL;
COrphan* porphan = nullptr;
double dPriority = 0;
int64_t nTotalIn = 0;
bool fMissingInputs = false;
@@ -210,7 +209,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
porphan->dFeePerKb = dFeePerKb;
}
else
vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second));
vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &tx));
}
// Collect transactions into block
@@ -247,7 +246,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
continue;
// Transaction fee
int64_t nMinFee = tx.GetMinFee(nBlockSize, GMF_BLOCK);
int64_t nMinFee = tx.GetMinFee(nBlockSize, GetMinFeeMode::Block);
// Skip free transactions if we're past the minimum block size:
if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
@@ -372,7 +371,7 @@ bool CheckStake(CBlock* pblock, CWallet& wallet)
}
// Process this block the same as if we had received it from another node
if (!ProcessBlock(NULL, pblock))
if (!ProcessBlock(nullptr, pblock))
return error("CheckStake() : ProcessBlock, block not accepted");
}
+44 -44
View File
@@ -47,7 +47,7 @@ void ThreadMapPort2(void* parg);
#endif
void ThreadHTTPSeedFetch(void* parg);
bool ThreadHTTPSeedFetch2(void* parg);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false);
struct LocalServiceInfo {
@@ -59,7 +59,7 @@ struct LocalServiceInfo {
// Global state variables
//
bool fClient = false;
//bool fDiscover = true;
#ifdef USE_UPNP
bool fUseUPnP = GetBoolArg("-upnp", USE_UPNP);
@@ -71,10 +71,10 @@ static CCriticalSection cs_mapLocalHost;
static map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfReachable[NET_MAX] = {};
static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
static CNode* pnodeLocalHost = nullptr;
CAddress addrSeenByPeer(CService("0.0.0.0", 0), nLocalServices);
uint64_t nLocalHostNonce = 0;
boost::array<int, THREAD_MAX> vnThreadsRunning;
std::array<int, THREAD_MAX> vnThreadsRunning;
static std::vector<SOCKET> vhListenSocket;
CAddrMan addrman;
@@ -91,7 +91,7 @@ CCriticalSection cs_vOneShots;
set<CNetAddr> setservAddNodeAddresses;
CCriticalSection cs_setservAddNodeAddresses;
static CSemaphore *semOutbound = NULL;
static CSemaphore *semOutbound = nullptr;
void AddOneShot(string strDest)
{
@@ -347,7 +347,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha
closesocket(hSocket);
return false;
}
if (pszKeyword == NULL)
if (pszKeyword == nullptr)
break;
if (strLine.find(pszKeyword) != string::npos)
{
@@ -423,7 +423,7 @@ bool GetMyExternalIP(CNetAddr& ipRet)
"Connection: close\r\n"
"\r\n";
pszKeyword = NULL; // Returns just IP address
pszKeyword = nullptr; // Returns just IP address
}
if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet))
@@ -469,7 +469,7 @@ CNode* FindNode(const CNetAddr& ip)
if ((CNetAddr)pnode->addr == ip)
return (pnode);
}
return NULL;
return nullptr;
}
CNode* FindNode(std::string addrName)
@@ -478,7 +478,7 @@ CNode* FindNode(std::string addrName)
for (CNode* pnode : vNodes)
if (pnode->addrName == addrName)
return (pnode);
return NULL;
return nullptr;
}
CNode* FindNode(const CService& addr)
@@ -489,7 +489,7 @@ CNode* FindNode(const CService& addr)
if ((CService)pnode->addr == addr)
return (pnode);
}
return NULL;
return nullptr;
}
CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
@@ -499,12 +499,12 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
if (addrStr.find(".onion") == std::string::npos) {
if (fDebug)
printf("ConnectNode(): REJECTED non-onion address: %s (Tor-native mode)\n", addrStr.c_str());
return NULL;
return nullptr;
}
if (pszDest == NULL) {
if (pszDest == nullptr) {
if (IsLocal(addrConnect))
return NULL;
return nullptr;
// Look for an existing connection
CNode* pnode = FindNode((CService)addrConnect);
@@ -557,7 +557,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
}
else
{
return NULL;
return nullptr;
}
}
@@ -1042,7 +1042,7 @@ void ThreadSocketHandler2(void* parg)
bool fIsSeed = false;
static const char *(*strOnionSeedCheck)[1] = fTestNet ? strTestNetOnionSeed : strMainNetOnionSeed;
std::string incomingAddr = addr.ToStringIP();
for (unsigned int si = 0; strOnionSeedCheck[si][0] != NULL; si++) {
for (unsigned int si = 0; strOnionSeedCheck[si][0] != nullptr; si++) {
if (incomingAddr.find(strOnionSeedCheck[si][0]) != std::string::npos) {
fIsSeed = true;
break;
@@ -1204,7 +1204,7 @@ void ThreadMapPort(void* parg)
PrintException(&e, "ThreadMapPort()");
} catch (...) {
vnThreadsRunning[THREAD_UPNP]--;
PrintException(NULL, "ThreadMapPort()");
PrintException(nullptr, "ThreadMapPort()");
}
printf("ThreadMapPort exited\n");
}
@@ -1325,7 +1325,7 @@ void MapPort()
printf("MapPort()...\n");
if (fUseUPnP && vnThreadsRunning[THREAD_UPNP] < 1)
{
if (!NewThread(ThreadMapPort, NULL))
if (!NewThread(ThreadMapPort, nullptr))
printf("Error: ThreadMapPort(ThreadMapPort) failed\n");
}
}
@@ -1403,7 +1403,7 @@ void ThreadOnionSeed(void* parg)
static const char *(*strOnionSeed)[1] = fTestNet ? strTestNetOnionSeed : strMainNetOnionSeed;
int found = 0;
for (unsigned int seed_idx = 0; strOnionSeed[seed_idx][0] != NULL; seed_idx++) {
for (unsigned int seed_idx = 0; strOnionSeed[seed_idx][0] != nullptr; seed_idx++) {
CNetAddr parsed;
if (!parsed.SetSpecial(strOnionSeed[seed_idx][0]))
throw runtime_error("ThreadOnionSeed() : invalid .onion seed");
@@ -1441,7 +1441,7 @@ void ThreadOnionSeed(void* parg)
MilliSleep(1000);
}
if (!fShutdown)
ok = ThreadHTTPSeedFetch2(NULL);
ok = ThreadHTTPSeedFetch2(nullptr);
}
if (!ok && !fShutdown)
printf("ThreadOnionSeed: all HTTPS seed fetch attempts failed\n");
@@ -1499,10 +1499,10 @@ void ThreadOnionSeed(void* parg)
else
printf("ThreadOnionSeed: low outbound peers (%d), re-seeding...\n", nOutbound);
ThreadHTTPSeedFetch2(NULL);
ThreadHTTPSeedFetch2(nullptr);
// Re-queue hardcoded seeds for direct connection
for (unsigned int seed_idx = 0; strOnionSeed[seed_idx][0] != NULL; seed_idx++) {
for (unsigned int seed_idx = 0; strOnionSeed[seed_idx][0] != nullptr; seed_idx++) {
std::string oneShotAddr = std::string(strOnionSeed[seed_idx][0])
+ ":" + std::to_string(GetDefaultPort());
AddOneShot(oneShotAddr);
@@ -1587,8 +1587,8 @@ bool ThreadHTTPSeedFetch2(void* parg)
printf("Fetching seed list from https://%s%s (via Tor)...\n", seedHost.c_str(), seedPath.c_str());
SSL_CTX* ctx = NULL;
SSL* ssl = NULL;
SSL_CTX* ctx = nullptr;
SSL* ssl = nullptr;
SOCKET hSocket = INVALID_SOCKET;
try {
@@ -1611,7 +1611,7 @@ bool ThreadHTTPSeedFetch2(void* parg)
// Use system default CA certificates for verification
SSL_CTX_set_default_verify_paths(ctx);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr);
ssl = SSL_new(ctx);
if (!ssl) {
@@ -1677,8 +1677,8 @@ bool ThreadHTTPSeedFetch2(void* parg)
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(hSocket);
ssl = NULL;
ctx = NULL;
ssl = nullptr;
ctx = nullptr;
hSocket = INVALID_SOCKET;
if (response.empty()) {
@@ -1785,7 +1785,7 @@ void ThreadHTTPSeedFetch(void* parg)
PrintException(&e, "ThreadHTTPSeedFetch()");
} catch (...) {
vnThreadsRunning[THREAD_HTTPSEED]--;
PrintException(NULL, "ThreadHTTPSeedFetch()");
PrintException(nullptr, "ThreadHTTPSeedFetch()");
}
printf("ThreadHTTPSeedFetch exited\n");
}
@@ -1806,7 +1806,7 @@ void ThreadOpenConnections(void* parg)
PrintException(&e, "ThreadOpenConnections()");
} catch (...) {
vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
PrintException(NULL, "ThreadOpenConnections()");
PrintException(nullptr, "ThreadOpenConnections()");
}
printf("ThreadOpenConnections exited\n");
}
@@ -1880,7 +1880,7 @@ void ThreadOpenConnections2(void* parg)
for (string strAddr : mapMultiArgs["-connect"])
{
CAddress addr;
OpenNetworkConnection(addr, NULL, strAddr.c_str());
OpenNetworkConnection(addr, nullptr, strAddr.c_str());
for (int i = 0; i < 10 && i < nLoop; i++)
{
MilliSleep(500);
@@ -1988,7 +1988,7 @@ void ThreadOpenAddedConnections(void* parg)
PrintException(&e, "ThreadOpenAddedConnections()");
} catch (...) {
vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--;
PrintException(NULL, "ThreadOpenAddedConnections()");
PrintException(nullptr, "ThreadOpenAddedConnections()");
}
printf("ThreadOpenAddedConnections exited\n");
}
@@ -2120,7 +2120,7 @@ void ThreadMessageHandler(void* parg)
PrintException(&e, "ThreadMessageHandler()");
} catch (...) {
vnThreadsRunning[THREAD_MESSAGEHANDLER]--;
PrintException(NULL, "ThreadMessageHandler()");
PrintException(nullptr, "ThreadMessageHandler()");
}
printf("ThreadMessageHandler exited\n");
}
@@ -2141,7 +2141,7 @@ void ThreadMessageHandler2(void* parg)
}
// Poll the connected nodes for messages
CNode* pnodeTrickle = NULL;
CNode* pnodeTrickle = nullptr;
if (!vNodesCopy.empty())
pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())];
for (CNode* pnode : vNodesCopy)
@@ -2386,7 +2386,7 @@ void StartNode(void* parg)
// Make this thread recognisable as the startup thread
RenameThread("Triangles-start");
if (semOutbound == NULL) {
if (semOutbound == nullptr) {
// initialize semaphore — use -maxoutbound if specified, else default
int nMaxOutbound = (int)GetArg("-maxoutbound", MAX_OUTBOUND_CONNECTIONS);
nMaxOutbound = min(nMaxOutbound, (int)GetArg("-maxconnections", 125));
@@ -2395,7 +2395,7 @@ void StartNode(void* parg)
semOutbound = new CSemaphore(nMaxOutbound);
}
if (pnodeLocalHost == NULL)
if (pnodeLocalHost == nullptr)
pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices));
printf("StartNode(): pnodeLocalHost addr: %s\n",
@@ -2411,7 +2411,7 @@ void StartNode(void* parg)
if (!GetBoolArg("-onionseed", true))
printf(".onion seeding disabled\n");
else
if (!NewThread(ThreadOnionSeed, NULL))
if (!NewThread(ThreadOnionSeed, nullptr))
printf("Error: NewThread(ThreadOnionSeed) failed\n");
// Map ports with UPnP (default)
@@ -2424,34 +2424,34 @@ void StartNode(void* parg)
printf("HTTP seed fetch handled by onion seed thread\n");
else if (GetBoolArg("-noseedurl", false))
printf("HTTP seed fetch disabled\n");
else if (!NewThread(ThreadHTTPSeedFetch, NULL))
else if (!NewThread(ThreadHTTPSeedFetch, nullptr))
printf("Error: NewThread(ThreadHTTPSeedFetch) failed\n");
// Send and receive from sockets, accept connections
if (!NewThread(ThreadSocketHandler, NULL))
if (!NewThread(ThreadSocketHandler, nullptr))
printf("Error: NewThread(ThreadSocketHandler) failed\n");
// Initiate outbound connections from -addnode
if (!NewThread(ThreadOpenAddedConnections, NULL))
if (!NewThread(ThreadOpenAddedConnections, nullptr))
printf("Error: NewThread(ThreadOpenAddedConnections) failed\n");
// Initiate outbound connections
if (!NewThread(ThreadOpenConnections, NULL))
if (!NewThread(ThreadOpenConnections, nullptr))
printf("Error: NewThread(ThreadOpenConnections) failed\n");
// Process messages
if (!NewThread(ThreadMessageHandler, NULL))
if (!NewThread(ThreadMessageHandler, nullptr))
printf("Error: NewThread(ThreadMessageHandler) failed\n");
// Dump network addresses
if (!NewThread(ThreadDumpAddress, NULL))
if (!NewThread(ThreadDumpAddress, nullptr))
printf("Error; NewThread(ThreadDumpAddress) failed\n");
// Mine proof-of-stake blocks in the background
if (!GetBoolArg("-stake", true))
printf("Staking disabled at startup (stake=0).\n");
else
if (!NewThread(ThreadStakeMiner, pwalletMain))
if (!NewThread(ThreadStakeMiner, pwalletMain.get()))
printf("Error: NewThread(ThreadStakeMiner) failed\n");
}
@@ -2567,8 +2567,8 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt
}
// Save original serialized message so newer versions are preserved
mapRelay.insert(std::make_pair(inv, ss));
vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv));
mapRelay.insert({inv, ss});
vRelayExpiration.push_back({GetTime() + 15 * 60, inv});
}
RelayInventory(inv);
+10 -136
View File
@@ -6,7 +6,7 @@
#define TRIANGLES_NET_H
#include <deque>
#include <boost/array.hpp>
#include <array>
#include <openssl/rand.h>
#ifndef WIN32
@@ -34,7 +34,7 @@ bool GetMyExternalIP(CNetAddr& ipRet);
void AddressCurrentlyConnected(const CService& addr);
CNode* FindNode(const CNetAddr& ip);
CNode* FindNode(const CService& ip);
CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL);
CNode* ConnectNode(CAddress addrConnect, const char *strDest = nullptr);
void MapPort();
unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string()));
@@ -63,10 +63,10 @@ bool AddLocal(const CService& addr, int nScore = LOCAL_NONE);
bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
bool SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr);
bool IsReachable(const CNetAddr &addr);
void SetReachable(enum Network net, bool fFlag = true);
CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
CAddress GetLocalAddress(const CNetAddr *paddrPeer = nullptr);
enum
@@ -98,7 +98,7 @@ extern bool fUseUPnP;
extern uint64_t nLocalServices;
extern uint64_t nLocalHostNonce;
extern CAddress addrSeenByPeer;
extern boost::array<int, THREAD_MAX> vnThreadsRunning;
extern std::array<int, THREAD_MAX> vnThreadsRunning;
extern CAddrMan addrman;
extern std::vector<CNode*> vNodes;
@@ -444,7 +444,7 @@ public:
nRequestTime = nNow;
else
nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
mapAskFor.insert(std::make_pair(nRequestTime, inv));
mapAskFor.insert({nRequestTime, inv});
}
@@ -523,141 +523,15 @@ public:
}
}
template<typename T1>
void PushMessage(const char* pszCommand, const T1& a1)
template<typename T1, typename... Args>
void PushMessage(const char* pszCommand, const T1& a1, const Args&... args)
{
try
{
BeginMessage(pszCommand);
ssSend << a1;
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)
{
try
{
BeginMessage(pszCommand);
ssSend << a1 << a2;
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2, typename T3>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)
{
try
{
BeginMessage(pszCommand);
ssSend << a1 << a2 << a3;
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2, typename T3, typename T4>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4)
{
try
{
BeginMessage(pszCommand);
ssSend << a1 << a2 << a3 << a4;
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2, typename T3, typename T4, typename T5>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5)
{
try
{
BeginMessage(pszCommand);
ssSend << a1 << a2 << a3 << a4 << a5;
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6)
{
try
{
BeginMessage(pszCommand);
ssSend << a1 << a2 << a3 << a4 << a5 << a6;
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7)
{
try
{
BeginMessage(pszCommand);
ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7;
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8)
{
try
{
BeginMessage(pszCommand);
ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8;
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9)
{
try
{
BeginMessage(pszCommand);
ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9;
using swallow = int[];
(void)swallow{0, ((void)(ssSend << args), 0)...};
EndMessage();
}
catch (...)
+11 -12
View File
@@ -13,7 +13,6 @@
#endif
#include "strlcpy.h"
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
using namespace std;
@@ -27,7 +26,7 @@ bool fNameLookup = false;
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
enum Network ParseNetwork(std::string net) {
boost::to_lower(net);
net = ToLower(net);
if (net == "ipv4") return NET_IPV4;
if (net == "ipv6") return NET_IPV6;
if (net == "tor") return NET_TOR;
@@ -42,7 +41,7 @@ void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos);
if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) {
char *endp = NULL;
char *endp = nullptr;
int n = strtol(in.c_str() + colon + 1, &endp, 10);
if (endp && *endp == 0 && n >= 0) {
in = in.substr(0, colon);
@@ -88,13 +87,13 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
# endif
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
#endif
struct addrinfo *aiRes = NULL;
int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes);
struct addrinfo *aiRes = nullptr;
int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes);
if (nErr)
return false;
struct addrinfo *aiTrav = aiRes;
while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions))
while (aiTrav != nullptr && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions))
{
if (aiTrav->ai_family == AF_INET)
{
@@ -384,7 +383,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(hSocket, &fdset);
int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout);
int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout);
if (nRet == 0)
{
printf("connection timeout\n");
@@ -454,7 +453,7 @@ bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) {
if (nSocksVersion != 0 && !addrProxy.IsValid())
return false;
LOCK(cs_proxyInfos);
proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion);
proxyInfo[net] = {addrProxy, nSocksVersion};
return true;
}
@@ -473,7 +472,7 @@ bool SetNameProxy(CService addrProxy, int nSocksVersion) {
if (nSocksVersion != 0 && !addrProxy.IsValid())
return false;
LOCK(cs_proxyInfos);
nameproxyInfo = std::make_pair(addrProxy, nSocksVersion);
nameproxyInfo = {addrProxy, nSocksVersion};
return true;
}
@@ -868,7 +867,7 @@ std::string CNetAddr::ToStringIP() const
unsigned char sha3hash[32];
unsigned int sha3len = 0;
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(mdctx, EVP_sha3_256(), NULL);
EVP_DigestInit_ex(mdctx, EVP_sha3_256(), nullptr);
EVP_DigestUpdate(mdctx, checksumInput, 48);
EVP_DigestFinal_ex(mdctx, sha3hash, &sha3len);
EVP_MD_CTX_free(mdctx);
@@ -890,7 +889,7 @@ std::string CNetAddr::ToStringIP() const
socklen_t socklen = sizeof(sockaddr);
if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) {
char name[1025] = "";
if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), NULL, 0, NI_NUMERICHOST))
if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), nullptr, 0, NI_NUMERICHOST))
return std::string(name);
}
if (IsIPv4())
@@ -1043,7 +1042,7 @@ static const int NET_UNKNOWN = NET_MAX + 0;
static const int NET_TEREDO = NET_MAX + 1;
int static GetExtNetwork(const CNetAddr *addr)
{
if (addr == NULL)
if (addr == nullptr)
return NET_UNKNOWN;
if (addr->IsRFC4380())
return NET_TEREDO;
+2 -2
View File
@@ -18,11 +18,11 @@ static const char *strMainNetOnionSeed[][1] = {
{"on4noksywc7b6cdbbxsp535l7j4cugunvlyz3iyhf6sfcg2qzaoy3eqd.onion"},
// Contabo seed 4
{"3uyzltm5cy7xzunncp3d7ariw75erabdnj4l3cxwvsxb6h4orc7eiqad.onion"},
{NULL}
{nullptr}
};
static const char *strTestNetOnionSeed[][1] = {
{NULL}
{nullptr}
};
#endif
+1 -1
View File
@@ -545,7 +545,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
int64_t nFee = nTransactionFee * (1 + (int64_t)nBytes / 1000);
// Min Fee
int64_t nMinFee = txDummy.GetMinFee(1, GMF_SEND, nBytes);
int64_t nMinFee = txDummy.GetMinFee(1, GetMinFeeMode::Send, nBytes);
nPayFee = max(nFee, nMinFee);
+142
View File
@@ -0,0 +1,142 @@
// Copyright (c) 2026 The Triangles developers
// Distributed under the MIT/X11 software license.
#include "hdseeddialog.h"
#include "walletmodel.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QLabel>
#include <QMessageBox>
#include <QFont>
HDSeedDialog::HDSeedDialog(QWidget *parent)
: QDialog(parent), model(0), seedText(0), statusLabel(0)
{
setWindowTitle(tr("HD Seed Phrase (BIP39)"));
resize(560, 360);
QVBoxLayout *layout = new QVBoxLayout(this);
QLabel *intro = new QLabel(tr(
"A 24-word seed phrase is a complete backup of this wallet. Anyone who has it "
"can spend your coins. Write it down on paper and keep it offline."), this);
intro->setWordWrap(true);
layout->addWidget(intro);
seedText = new QPlainTextEdit(this);
seedText->setPlaceholderText(tr(
"Your 24-word phrase appears here when you generate or reveal it. "
"To restore, paste an existing 24-word phrase here and click 'Restore from Phrase'."));
QFont mono("monospace");
mono.setStyleHint(QFont::Monospace);
seedText->setFont(mono);
layout->addWidget(seedText);
statusLabel = new QLabel(this);
statusLabel->setWordWrap(true);
layout->addWidget(statusLabel);
QHBoxLayout *btns = new QHBoxLayout();
QPushButton *genBtn = new QPushButton(tr("Generate New"), this);
QPushButton *showBtn = new QPushButton(tr("Reveal for Backup"), this);
QPushButton *restoreBtn = new QPushButton(tr("Restore from Phrase"), this);
QPushButton *closeBtn = new QPushButton(tr("Close"), this);
btns->addWidget(genBtn);
btns->addWidget(showBtn);
btns->addWidget(restoreBtn);
btns->addStretch();
btns->addWidget(closeBtn);
layout->addLayout(btns);
connect(genBtn, SIGNAL(clicked()), this, SLOT(onGenerate()));
connect(showBtn, SIGNAL(clicked()), this, SLOT(onShow()));
connect(restoreBtn, SIGNAL(clicked()), this, SLOT(onRestore()));
connect(closeBtn, SIGNAL(clicked()), this, SLOT(accept()));
}
void HDSeedDialog::setModel(WalletModel *modelIn)
{
model = modelIn;
refreshStatus();
}
void HDSeedDialog::refreshStatus()
{
if (!model || !statusLabel) return;
if (model->hdEnabled())
statusLabel->setText(tr("Status: HD seed is ACTIVE. Use 'Reveal for Backup' to view your phrase."));
else
statusLabel->setText(tr("Status: no HD seed yet. Use 'Generate New' to create one."));
}
void HDSeedDialog::onGenerate()
{
if (!model) return;
if (model->hdEnabled()) {
QMessageBox::warning(this, tr("HD seed already set"),
tr("This wallet already has an HD seed. Use 'Reveal for Backup' to view it."));
return;
}
if (QMessageBox::question(this, tr("Generate new seed"),
tr("Generate a new 24-word HD seed for this wallet?"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return;
WalletModel::UnlockContext ctx(model->requestUnlock());
if (!ctx.isValid()) return;
QString mnemonic, err;
if (!model->hdNew(mnemonic, err)) {
QMessageBox::critical(this, tr("Error"), err);
return;
}
seedText->setPlainText(mnemonic);
QMessageBox::information(this, tr("Write this down"),
tr("Your new 24-word seed phrase is shown above. Write it on paper and store it safely "
"and offline. This is the only backup of this wallet."));
refreshStatus();
}
void HDSeedDialog::onShow()
{
if (!model) return;
WalletModel::UnlockContext ctx(model->requestUnlock());
if (!ctx.isValid()) return;
QString mnemonic, err;
if (!model->hdShow(mnemonic, err)) {
QMessageBox::critical(this, tr("Error"), err);
return;
}
seedText->setPlainText(mnemonic);
}
void HDSeedDialog::onRestore()
{
if (!model) return;
QString phrase = seedText->toPlainText().trimmed();
if (phrase.isEmpty()) {
QMessageBox::warning(this, tr("No phrase"),
tr("Paste a 24-word phrase into the box first."));
return;
}
if (QMessageBox::question(this, tr("Restore from phrase"),
tr("Restore the HD seed from the phrase in the box and rescan the chain? "
"This replaces the current HD seed."),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return;
WalletModel::UnlockContext ctx(model->requestUnlock());
if (!ctx.isValid()) return;
QString err;
if (!model->hdRestore(phrase, err)) {
QMessageBox::critical(this, tr("Error"), err);
return;
}
QMessageBox::information(this, tr("Restored"),
tr("HD seed restored and the chain was rescanned for your funds."));
refreshStatus();
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2026 The Triangles developers
// Distributed under the MIT/X11 software license.
#ifndef HDSEEDDIALOG_H
#define HDSEEDDIALOG_H
#include <QDialog>
class WalletModel;
QT_BEGIN_NAMESPACE
class QPlainTextEdit;
class QLabel;
QT_END_NAMESPACE
/** Generate, reveal (for backup), and restore the wallet's BIP39 HD seed phrase. */
class HDSeedDialog : public QDialog
{
Q_OBJECT
public:
explicit HDSeedDialog(QWidget *parent = 0);
void setModel(WalletModel *model);
private:
WalletModel *model;
QPlainTextEdit *seedText;
QLabel *statusLabel;
void refreshStatus();
private slots:
void onGenerate();
void onShow();
void onRestore();
};
#endif // HDSEEDDIALOG_H
+4 -2
View File
@@ -13,7 +13,6 @@
#include "ui_interface.h"
#include "util.h"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/version.hpp>
@@ -26,6 +25,9 @@ using namespace boost;
using namespace boost::interprocess;
using namespace boost::posix_time;
#include <algorithm>
#include <cctype>
#if defined MAC_OSX || defined __FreeBSD__
// URI handling not implemented on OSX yet
@@ -42,7 +44,7 @@ static bool ipcScanCmd(int argc, char *argv[], bool fRelay)
bool fSent = false;
for (int i = 1; i < argc; i++)
{
if (boost::algorithm::istarts_with(argv[i], "Triangles:"))
if (std::equal(std::begin("Triangles:"), std::end("Triangles:") - 1, argv[i], [](char a, char b) { return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b)); }))
{
const char *strURI = argv[i];
try {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 795 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 17 KiB

+1 -1
View File
@@ -229,7 +229,7 @@ int main(int argc, char *argv[])
// calling Shutdown().
ClientModel clientModel(&optionsModel);
WalletModel walletModel(pwalletMain, &optionsModel);
WalletModel walletModel(pwalletMain.get(), &optionsModel);
window.setClientModel(&clientModel);
window.setWalletModel(&walletModel);
+22 -2
View File
@@ -31,6 +31,7 @@
#include "trianglesunits.h"
#include "guiconstants.h"
#include "askpassphrasedialog.h"
#include "hdseeddialog.h"
#include "notificator.h"
#include "guiutil.h"
#include "rpcconsole.h"
@@ -89,7 +90,8 @@
#include <iostream>
extern CWallet* pwalletMain;
#include <memory>
extern std::unique_ptr<CWallet> pwalletMain;
extern int64_t nLastCoinStakeSearchInterval;
extern unsigned int nTargetSpacing;
double GetPoSKernelPS();
@@ -483,6 +485,8 @@ void TrianglesGUI::createActions(bool fIsTestnet)
backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
changePassphraseAction = new QAction(QIcon(":/menu_16/passphrase"), tr("&Change Passphrase..."), this);
changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
hdSeedAction = new QAction(QIcon(":/menu_16/passphrase"), tr("&Seed Phrase (HD Backup)..."), this);
hdSeedAction->setStatusTip(tr("Generate, restore, or back up your 24-word HD seed phrase"));
unlockWalletAction = new QAction(QIcon(":/menu_16/unlock"), tr("&Unlock Wallet..."), this);
unlockWalletAction->setStatusTip(tr("Unlock wallet"));
unlockWalletStakingAction = new QAction(QIcon(":/menu_16/unlock"), tr("&Unlock Wallet for staking..."), this);
@@ -508,6 +512,7 @@ void TrianglesGUI::createActions(bool fIsTestnet)
connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool)));
connect(backupWalletAction, SIGNAL(triggered()), this, SLOT(backupWallet()));
connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase()));
connect(hdSeedAction, SIGNAL(triggered()), this, SLOT(hdSeedManager()));
connect(unlockWalletAction, SIGNAL(triggered()), this, SLOT(unlockWallet()));
connect(unlockWalletStakingAction, SIGNAL(triggered()), this, SLOT(unlockWalletStaking()));
connect(lockWalletAction, SIGNAL(triggered()), this, SLOT(lockWallet()));
@@ -537,6 +542,7 @@ void TrianglesGUI::createMenuBar()
QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
settings->addAction(encryptWalletAction);
settings->addAction(changePassphraseAction);
settings->addAction(hdSeedAction);
settings->addAction(unlockWalletAction);
settings->addAction(lockWalletAction);
settings->addSeparator();
@@ -655,7 +661,7 @@ void TrianglesGUI::ensureMessageModel()
if(messageModel || !walletModel)
return;
setMessageModel(new MessageModel(pwalletMain, walletModel, this));
setMessageModel(new MessageModel(pwalletMain.get(), walletModel, this));
}
void TrianglesGUI::ensureSendCoinsPage()
@@ -1447,6 +1453,7 @@ void TrianglesGUI::menuOperationsRequested()
QAction* unlockWalletStaking = menu.addAction(QIcon(":/menu_16/unlock"), tr("&Unlock Wallet...").remove('&').remove("..."));
QAction* lockWallet = menu.addAction(QIcon(":/menu_16/lock"), tr("&Lock Wallet...").remove('&').remove("..."));
QAction* changePassword = menu.addAction(QIcon(":/menu_16/passphrase"), tr("&Change Passphrase...").remove('&').remove("..."));
QAction* hdSeed = menu.addAction(QIcon(":/menu_16/passphrase"), tr("Seed Phrase (HD Backup)..."));
QAction* signMessage = menu.addAction(QIcon(":/menu_16/sign"), tr("Sign &message...").remove('&').remove("..."));
QAction* verifySignature = menu.addAction(QIcon(":/menu_16/verify"), tr("&Verify message...").remove('&').remove("..."));
@@ -1507,6 +1514,10 @@ void TrianglesGUI::menuOperationsRequested()
if (walletModel->getEncryptionStatus() == WalletModel::Unlocked || walletModel->getEncryptionStatus() == WalletModel::Locked)
changePassphrase();
}
else if (selected == hdSeed)
{
hdSeedManager();
}
else if (selected == signMessage)
{
gotoSignMessageTab();
@@ -1613,6 +1624,15 @@ void TrianglesGUI::changePassphrase()
dlg.exec();
}
void TrianglesGUI::hdSeedManager()
{
if (!walletModel)
return;
HDSeedDialog dlg(this);
dlg.setModel(walletModel);
dlg.exec();
}
void TrianglesGUI::unlockWalletStaking()
{
+3
View File
@@ -131,6 +131,7 @@ private:
QAction *encryptWalletAction;
QAction *backupWalletAction;
QAction *changePassphraseAction;
QAction *hdSeedAction;
QAction *unlockWalletAction;
QAction *unlockWalletStakingAction;
QAction *lockWalletAction;
@@ -243,6 +244,8 @@ private slots:
void backupWallet();
/** Change encrypted wallet passphrase */
void changePassphrase();
/** Open the HD seed phrase (generate/restore/backup) dialog */
void hdSeedManager();
/** Ask for passphrase to unlock wallet temporarily */
void unlockWallet();
/** Ask for passphrase to unlock wallet temporarily - FOR STAKING ONLY */
+46
View File
@@ -676,3 +676,49 @@ void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
{
return;
}
// ---- HD wallet (BIP39/BIP32) ----
bool WalletModel::hdEnabled() const
{
return wallet->IsHDEnabled();
}
bool WalletModel::hdNew(QString &mnemonicOut, QString &errorOut)
{
std::string mnemonic, strError;
if (!wallet->SetHDSeed("", "", true, mnemonic, strError)) {
errorOut = QString::fromStdString(strError);
return false;
}
wallet->TopUpKeyPool();
mnemonicOut = QString::fromStdString(mnemonic);
return true;
}
bool WalletModel::hdRestore(const QString &mnemonic, QString &errorOut)
{
std::string out, strError;
if (!wallet->SetHDSeed(mnemonic.toStdString(), "", false, out, strError)) {
errorOut = QString::fromStdString(strError);
return false;
}
wallet->TopUpKeyPool();
{
LOCK2(cs_main, wallet->cs_wallet);
wallet->ScanForWalletTransactions(pindexGenesisBlock, true);
wallet->ReacceptWalletTransactions();
}
return true;
}
bool WalletModel::hdShow(QString &mnemonicOut, QString &errorOut)
{
std::string mnemonic;
if (!wallet->GetHDMnemonic(mnemonic)) {
errorOut = QObject::tr("Wallet has no HD seed (use 'Generate New').");
return false;
}
mnemonicOut = QString::fromStdString(mnemonic);
return true;
}
+7 -1
View File
@@ -93,7 +93,7 @@ public:
};
// Send coins to a list of recipients
SendCoinsReturn sendCoins(const QList<SendCoinsRecipient> &recipients, const CCoinControl *coinControl=NULL);
SendCoinsReturn sendCoins(const QList<SendCoinsRecipient> &recipients, const CCoinControl *coinControl=nullptr);
// Wallet encryption
bool setWalletEncrypted(bool encrypted, const SecureString &passphrase);
@@ -103,6 +103,12 @@ public:
// Wallet backup
bool backupWallet(const QString &filename);
// ---- HD wallet (BIP39/BIP32) ----
bool hdEnabled() const;
bool hdNew(QString &mnemonicOut, QString &errorOut);
bool hdRestore(const QString &mnemonic, QString &errorOut);
bool hdShow(QString &mnemonicOut, QString &errorOut);
// RAI object for unlocking wallet, returned by requestUnlock()
class UnlockContext
{
+7 -10
View File
@@ -12,8 +12,6 @@
#include "wallet.h"
#include "init.h"
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace json_spirit;
@@ -95,7 +93,7 @@ bool CheckRESTRateLimit(const string& strIP)
string HTTPReplyREST(int nStatus, const string& strMsg, const string& contentType)
{
string strCorsOrigin = GetArg("-restcorsorigin", "*");
string strCorsOrigin = GetArg(std::string_view{"-restcorsorigin"}, std::string_view{"*"});
const char *cStatus;
if (nStatus == 200) cStatus = "OK";
@@ -149,12 +147,11 @@ static void ParseRESTPath(const string& strURI, vector<string>& parts, map<strin
}
// Split path into parts
boost::split(parts, path, boost::is_any_of("/"));
parts = SplitString(path, '/');
// Parse query parameters
if (!queryString.empty()) {
vector<string> pairs;
boost::split(pairs, queryString, boost::is_any_of("&"));
auto pairs = SplitString(queryString, '&');
for (size_t i = 0; i < pairs.size(); i++) {
size_t eq = pairs[i].find('=');
if (eq != string::npos)
@@ -175,12 +172,12 @@ bool IsRESTPath(const string& strURI)
static bool RESTAuthorized(map<string, string>& mapHeaders)
{
// Check Bearer token first (if -restapikey is set)
string strApiKey = GetArg("-restapikey", "");
string strApiKey = GetArg(std::string_view{"-restapikey"}, std::string_view{""});
if (!strApiKey.empty()) {
string strAuth = mapHeaders.count("authorization") ? mapHeaders["authorization"] : "";
if (strAuth.substr(0, 7) == "Bearer ") {
string strToken = strAuth.substr(7);
boost::trim(strToken);
strToken = TrimString(strToken);
if (TimingResistantEqual(strToken, strApiKey))
return true;
}
@@ -300,8 +297,8 @@ static bool HandleBlockHeader(const string& param, string& strReply, int& nStatu
result.push_back(Pair("height", pblockindex->nHeight));
result.push_back(Pair("version", pblockindex->nVersion));
result.push_back(Pair("merkleroot", pblockindex->hashMerkleRoot.GetHex()));
result.push_back(Pair("time", (boost::int64_t)pblockindex->GetBlockTime()));
result.push_back(Pair("nonce", (boost::uint64_t)pblockindex->nNonce));
result.push_back(Pair("time", (int64_t)pblockindex->GetBlockTime()));
result.push_back(Pair("nonce", (uint64_t)pblockindex->nNonce));
result.push_back(Pair("bits", HexBits(pblockindex->nBits)));
result.push_back(Pair("difficulty", GetDifficulty(pblockindex)));
result.push_back(Pair("flags", strprintf("%s%s",
+10 -10
View File
@@ -34,15 +34,15 @@ double GetDifficulty(const CBlockIndex* blockindex)
{
// Floating point number that is a multiple of the minimum difficulty,
// minimum difficulty = 1.0.
if (blockindex == NULL)
if (blockindex == nullptr)
{
if (pindexBest == NULL)
if (pindexBest == nullptr)
return 1.0;
else
blockindex = GetLastBlockIndex(pindexBest, false);
}
if (blockindex == NULL)
if (blockindex == nullptr)
return 1.0;
int nShift = (blockindex->nBits >> 24) & 0xff;
@@ -98,7 +98,7 @@ double GetPoSKernelPS()
int nStakesHandled = 0, nStakesTime = 0;
CBlockIndex* pindex = pindexBest;;
CBlockIndex* pindexPrevStake = NULL;
CBlockIndex* pindexPrevStake = nullptr;
while (pindex && nStakesHandled < nPoSInterval)
{
@@ -128,8 +128,8 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool fPri
result.push_back(Pair("version", block.nVersion));
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
result.push_back(Pair("mint", ValueFromAmount(blockindex->nMint)));
result.push_back(Pair("time", (boost::int64_t)block.GetBlockTime()));
result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce));
result.push_back(Pair("time", (int64_t)block.GetBlockTime()));
result.push_back(Pair("nonce", (uint64_t)block.nNonce));
result.push_back(Pair("bits", HexBits(block.nBits)));
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
result.push_back(Pair("blocktrust", leftTrim(blockindex->GetBlockTrust().GetHex(), '0')));
@@ -330,8 +330,8 @@ Value getblockheader(const Array& params, bool fHelp)
result.push_back(Pair("version", pblockindex->nVersion));
result.push_back(Pair("merkleroot", pblockindex->hashMerkleRoot.GetHex()));
result.push_back(Pair("mint", ValueFromAmount(pblockindex->nMint)));
result.push_back(Pair("time", (boost::int64_t)pblockindex->GetBlockTime()));
result.push_back(Pair("nonce", (boost::uint64_t)pblockindex->nNonce));
result.push_back(Pair("time", (int64_t)pblockindex->GetBlockTime()));
result.push_back(Pair("nonce", (uint64_t)pblockindex->nNonce));
result.push_back(Pair("bits", HexBits(pblockindex->nBits)));
result.push_back(Pair("difficulty", GetDifficulty(pblockindex)));
result.push_back(Pair("blocktrust", leftTrim(pblockindex->GetBlockTrust().GetHex(), '0')));
@@ -708,7 +708,7 @@ Value getblockchaininfo(const Array& params, bool fHelp)
obj.push_back(Pair("difficulty", diff));
obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
obj.push_back(Pair("timeoffset", (int64_t)GetTimeOffset()));
obj.push_back(Pair("connections", (int)vNodes.size()));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
return obj;
@@ -1042,7 +1042,7 @@ Value invalidateblock(const Array& params, bool fHelp)
setStakeSeen.erase(make_pair(pindexWalk->prevoutStake, pindexWalk->nStakeTime));
}
pindexWalk->pprev->pnext = NULL;
pindexWalk->pprev->pnext = nullptr;
pindexWalk = pindexWalk->pprev;
}
+6 -8
View File
@@ -11,7 +11,6 @@
#include "base58.h"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/algorithm/string.hpp>
#define printf OutputDebugStringF
@@ -94,9 +93,9 @@ public:
bool fSpent;
CWalletTx* ptx;
int nOut;
CTxDump(CWalletTx* ptx = NULL, int nOut = -1)
CTxDump(CWalletTx* ptx = nullptr, int nOut = -1)
{
pindex = NULL;
pindex = nullptr;
nValue = 0;
fSpent = false;
this->ptx = ptx;
@@ -167,8 +166,7 @@ Value importwallet(const Array& params, bool fHelp)
if (line.empty() || line[0] == '#')
continue;
std::vector<std::string> vstr;
boost::split(vstr, line, boost::is_any_of(" "));
auto vstr = SplitString(line, ' ');
if (vstr.size() < 2)
continue;
CTrianglesSecret vchSecret;
@@ -189,13 +187,13 @@ Value importwallet(const Array& params, bool fHelp)
std::string strLabel;
bool fLabel = true;
for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
if (boost::algorithm::starts_with(vstr[nStr], "#"))
if (vstr[nStr].starts_with("#"))
break;
if (vstr[nStr] == "change=1")
fLabel = false;
if (vstr[nStr] == "reserve=1")
fLabel = false;
if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
if (vstr[nStr].starts_with("label=")) {
strLabel = DecodeDumpString(vstr[nStr].substr(6));
fLabel = true;
}
@@ -281,7 +279,7 @@ Value dumpwallet(const Array& params, bool fHelp)
// sort time/key pairs
std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
vKeyBirth.push_back(std::make_pair(it->second, it->first));
vKeyBirth.push_back({it->second, it->first});
}
mapKeyBirth.clear();
std::sort(vKeyBirth.begin(), vKeyBirth.end());
+8 -8
View File
@@ -31,7 +31,7 @@ Value getnetworkinfo(const Array& params, bool fHelp)
healthObj.push_back(Pair("torpeers", health.torPeers));
healthObj.push_back(Pair("bootstrapped", health.isBootstrapped));
healthObj.push_back(Pair("syncing", health.isSyncing));
healthObj.push_back(Pair("lastblocktime", static_cast<boost::int64_t>(health.lastBlockTime)));
healthObj.push_back(Pair("lastblocktime", static_cast<int64_t>(health.lastBlockTime)));
healthObj.push_back(Pair("networkmode", "tor_native"));
Object obj;
@@ -88,9 +88,9 @@ Value getpeerinfo(const Array& params, bool fHelp)
obj.push_back(Pair("addr", stats.addrName));
obj.push_back(Pair("services", strprintf("%08"PRIx64, stats.nServices)));
obj.push_back(Pair("lastsend", (boost::int64_t)stats.nLastSend));
obj.push_back(Pair("lastrecv", (boost::int64_t)stats.nLastRecv));
obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected));
obj.push_back(Pair("lastsend", (int64_t)stats.nLastSend));
obj.push_back(Pair("lastrecv", (int64_t)stats.nLastRecv));
obj.push_back(Pair("conntime", (int64_t)stats.nTimeConnected));
obj.push_back(Pair("version", stats.nVersion));
obj.push_back(Pair("subver", stats.strSubVer));
obj.push_back(Pair("inbound", stats.fInbound));
@@ -195,7 +195,7 @@ Value getseedlist(const Array& params, bool fHelp)
Object obj;
obj.push_back(Pair("address", addr.ToStringIP()));
obj.push_back(Pair("port", (int)addr.GetPort()));
obj.push_back(Pair("lastseen", (boost::int64_t)addr.nTime));
obj.push_back(Pair("lastseen", (int64_t)addr.nTime));
ret.push_back(obj);
}
@@ -274,11 +274,11 @@ Value getnetworkstability(const Array& params, bool fHelp)
obj.push_back(Pair("ping", pingObj));
Object uptimeObj;
uptimeObj.push_back(Pair("newest_sec", nTotal > 0 ? (boost::int64_t)nNewestConnection : 0));
uptimeObj.push_back(Pair("oldest_sec", nTotal > 0 ? (boost::int64_t)nOldestConnection : 0));
uptimeObj.push_back(Pair("newest_sec", nTotal > 0 ? (int64_t)nNewestConnection : 0));
uptimeObj.push_back(Pair("oldest_sec", nTotal > 0 ? (int64_t)nOldestConnection : 0));
obj.push_back(Pair("connection_uptime", uptimeObj));
obj.push_back(Pair("seconds_since_last_block", (boost::int64_t)(GetTime() - nTimeBestReceived)));
obj.push_back(Pair("seconds_since_last_block", (int64_t)(GetTime() - nTimeBestReceived)));
obj.push_back(Pair("current_height", nBestHeight));
return obj;
+10 -10
View File
@@ -17,7 +17,7 @@ using namespace json_spirit;
void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex)
{
txnouttype type;
TxnOutType type;
vector<CTxDestination> addresses;
int nRequired;
@@ -28,7 +28,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
{
out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
out.push_back(Pair("type", GetTxnOutputType(TxnOutType::NonStandard)));
return;
}
@@ -45,8 +45,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
{
entry.push_back(Pair("txid", tx.GetHash().GetHex()));
entry.push_back(Pair("version", tx.nVersion));
entry.push_back(Pair("time", (boost::int64_t)tx.nTime));
entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime));
entry.push_back(Pair("time", (int64_t)tx.nTime));
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
Array vin;
for (const CTxIn& txin : tx.vin)
{
@@ -56,13 +56,13 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
else
{
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
in.push_back(Pair("vout", (boost::int64_t)txin.prevout.n));
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
Object o;
o.push_back(Pair("asm", txin.scriptSig.ToString()));
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
in.push_back(Pair("scriptSig", o));
}
in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence));
in.push_back(Pair("sequence", (int64_t)txin.nSequence));
vin.push_back(in);
}
entry.push_back(Pair("vin", vin));
@@ -72,7 +72,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
const CTxOut& txout = tx.vout[i];
Object out;
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
out.push_back(Pair("n", (boost::int64_t)i));
out.push_back(Pair("n", (int64_t)i));
Object o;
ScriptPubKeyToJSON(txout.scriptPubKey, o, false);
out.push_back(Pair("scriptPubKey", o));
@@ -90,8 +90,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
if (pindex->IsInMainChain())
{
entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime));
entry.push_back(Pair("time", (int64_t)pindex->nTime));
entry.push_back(Pair("blocktime", (int64_t)pindex->nTime));
}
else
entry.push_back(Pair("confirmations", 0));
@@ -552,7 +552,7 @@ Value sendrawtransaction(const Array& params, bool fHelp)
if (!tx.AcceptToMemoryPool(txdb))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
SyncWithWallets(tx, NULL, true);
SyncWithWallets(tx, nullptr, true);
}
RelayTransaction(tx, hashTx);
+89 -14
View File
@@ -59,11 +59,11 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
entry.push_back(Pair("blockindex", wtx.nIndex));
auto mi = mapBlockIndex.find(wtx.hashBlock);
if (mi != mapBlockIndex.end() && mi->second)
entry.push_back(Pair("blocktime", (boost::int64_t)(mi->second->nTime)));
entry.push_back(Pair("blocktime", (int64_t)(mi->second->nTime)));
}
entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
entry.push_back(Pair("time", (int64_t)wtx.GetTxTime()));
entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));
for (const auto& item : wtx.mapValue)
entry.push_back(Pair(item.first, item.second));
}
@@ -94,7 +94,7 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
obj.push_back(Pair("blocks", (int)nBestHeight));
obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
obj.push_back(Pair("timeoffset", (int64_t)GetTimeOffset()));
obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
obj.push_back(Pair("connections", (int)vNodes.size()));
obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
@@ -105,14 +105,14 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("difficulty", diff));
obj.push_back(Pair("testnet", fTestNet));
obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
obj.push_back(Pair("keypoololdest", (int64_t)pwalletMain->GetOldestKeyPoolTime()));
obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
if (pwalletMain->IsCrypted())
{
LOCK(cs_nWalletUnlockTime);
obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
obj.push_back(Pair("unlocked_until", (int64_t)nWalletUnlockTime / 1000));
}
obj.push_back(Pair("errors", GetWarnings("statusbar")));
return obj;
@@ -139,14 +139,14 @@ Value getwalletinfo(const Array& params, bool fHelp)
obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
obj.push_back(Pair("txcount", txCount));
obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
obj.push_back(Pair("keypoololdest", (int64_t)pwalletMain->GetOldestKeyPoolTime()));
obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
if (pwalletMain->IsCrypted())
{
LOCK(cs_nWalletUnlockTime);
obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
obj.push_back(Pair("unlocked_until", (int64_t)nWalletUnlockTime / 1000));
}
return obj;
}
@@ -818,7 +818,7 @@ Value sendmany(const Array& params, bool fHelp)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
// Send
CReserveKey keyChange(pwalletMain);
CReserveKey keyChange(pwalletMain.get());
int64_t nFeeRequired = 0;
bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
if (!fCreated)
@@ -1151,7 +1151,7 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Ar
Object entry;
entry.push_back(Pair("account", acentry.strAccount));
entry.push_back(Pair("category", "move"));
entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
entry.push_back(Pair("time", (int64_t)acentry.nTime));
entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
entry.push_back(Pair("comment", acentry.strComment));
@@ -1284,7 +1284,7 @@ Value listsinceblock(const Array& params, bool fHelp)
"listsinceblock [blockhash] [target-confirmations]\n"
"Get all transactions in blocks since block [blockhash], or all transactions if omitted");
CBlockIndex *pindex = NULL;
CBlockIndex *pindex = nullptr;
int target_confirms = 1;
if (params.size() > 0)
@@ -1535,7 +1535,7 @@ Value walletpassphrase(const Array& params, bool fHelp)
"walletpassphrase <passphrase> <timeout>\n"
"Stores the wallet decryption key in memory for <timeout> seconds.");
NewThread(ThreadTopUpKeyPool, NULL);
NewThread(ThreadTopUpKeyPool, nullptr);
int64_t* pnSleepTime = new int64_t(params[1].get_int64());
NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
@@ -1654,7 +1654,7 @@ public:
CScript subscript;
pwalletMain->GetCScript(scriptID, subscript);
std::vector<CTxDestination> addresses;
txnouttype whichType;
TxnOutType whichType;
int nRequired;
ExtractDestinations(subscript, whichType, addresses, nRequired);
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
@@ -1663,7 +1663,7 @@ public:
for (const CTxDestination& addr : addresses)
a.push_back(CTrianglesAddress(addr).ToString());
obj.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG)
if (whichType == TxnOutType::MultiSig)
obj.push_back(Pair("sigsrequired", nRequired));
return obj;
}
@@ -1858,3 +1858,78 @@ Value makekeypair(const Array& params, bool fHelp)
result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
return result;
}
Value hdinfo(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error("hdinfo\nReturns HD (BIP39/BIP32) wallet status.");
Object obj;
obj.push_back(Pair("hdenabled", pwalletMain->IsHDEnabled()));
obj.push_back(Pair("coin_type", 2222));
obj.push_back(Pair("derivation_path", "m/44'/2222'/0'/0/i"));
obj.push_back(Pair("nextindex", (int64_t)pwalletMain->nHDChainIndex));
return obj;
}
Value hdnew(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"hdnew [passphrase]\n"
"Generate a NEW 24-word HD seed phrase, activate it as this wallet's\n"
"deterministic seed, and return the phrase. WRITE IT DOWN: it is the\n"
"only backup of every address this wallet derives.");
EnsureWalletIsUnlocked();
if (pwalletMain->IsHDEnabled())
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet already has an HD seed; use 'hdshow' to back it up.");
string passphrase = params.size() > 0 ? params[0].get_str() : "";
string mnemonic, strError;
if (!pwalletMain->SetHDSeed("", passphrase, true, mnemonic, strError))
throw JSONRPCError(RPC_WALLET_ERROR, strError);
pwalletMain->TopUpKeyPool();
Object obj;
obj.push_back(Pair("mnemonic", mnemonic));
obj.push_back(Pair("words", 24));
obj.push_back(Pair("warning", "Write these 24 words down and keep them secret and offline. Anyone with them can spend your coins."));
return obj;
}
Value hdrestore(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"hdrestore \"mnemonic\" [passphrase]\n"
"Activate an HD seed from an existing 24-word phrase, derive the keypool\n"
"and rescan the chain for funds on the derived addresses.");
EnsureWalletIsUnlocked();
string mnemonic = params[0].get_str();
string passphrase = params.size() > 1 ? params[1].get_str() : "";
string out, strError;
if (!pwalletMain->SetHDSeed(mnemonic, passphrase, false, out, strError))
throw JSONRPCError(RPC_WALLET_ERROR, strError);
pwalletMain->TopUpKeyPool();
{
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
pwalletMain->ReacceptWalletTransactions();
}
Object obj;
obj.push_back(Pair("restored", true));
return obj;
}
Value hdshow(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"hdshow\nReveal the wallet's HD mnemonic for backup. The wallet must be unlocked.");
EnsureWalletIsUnlocked();
string mnemonic;
if (!pwalletMain->GetHDMnemonic(mnemonic))
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet has no HD seed (use 'hdnew' to create one).");
Object obj;
obj.push_back(Pair("mnemonic", mnemonic));
obj.push_back(Pair("warning", "Keep these words secret and offline."));
return obj;
}
+73 -79
View File
@@ -16,7 +16,7 @@ using namespace std;
#include "sync.h"
#include "util.h"
bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
bool CheckSig(const vector<unsigned char>& vchSig, const vector<unsigned char>& vchPubKey, const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
static const valtype vchFalse(0);
static const valtype vchZero(0);
@@ -93,17 +93,17 @@ static inline void popstack(vector<valtype>& stack)
}
const char* GetTxnOutputType(txnouttype t)
const char* GetTxnOutputType(TxnOutType t)
{
switch (t)
{
case TX_NONSTANDARD: return "nonstandard";
case TX_PUBKEY: return "pubkey";
case TX_PUBKEYHASH: return "pubkeyhash";
case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig";
case TxnOutType::NonStandard: return "nonstandard";
case TxnOutType::PubKey: return "pubkey";
case TxnOutType::PubKeyHash: return "pubkeyhash";
case TxnOutType::ScriptHash: return "scripthash";
case TxnOutType::MultiSig: return "multisig";
}
return NULL;
return nullptr;
}
@@ -751,8 +751,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
if (stack.size() < 1)
return false;
valtype& vch = stacktop(-1);
for (unsigned int i = 0; i < vch.size(); i++)
vch[i] = ~vch[i];
for (auto& b : vch)
b = ~b;
}
break;
@@ -893,7 +893,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
break;
case OP_DIV:
if (!BN_div(bn.get(), NULL, bn1.get(), bn2.get(), pctx))
if (!BN_div(bn.get(), nullptr, bn1.get(), bn2.get(), pctx))
return false;
break;
@@ -973,7 +973,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
valtype& vch = stacktop(-1);
valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32);
if (opcode == OP_RIPEMD160)
{
TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN
RIPEMD160(&vch[0], vch.size(), &vchHash[0]);
TRI_OPENSSL_SUPPRESS_DEPRECATED_END
}
else if (opcode == OP_SHA1)
SHA1(&vch[0], vch.size(), &vchHash[0]);
else if (opcode == OP_SHA256)
@@ -1227,7 +1231,7 @@ private:
// Mix sighash with first 8 bytes of sig and pubkey for a fast key
uint64_t k = hash.Get64();
if (vchSig.size() >= 8)
memcpy(&k, &k, 4); // keep upper half
k = (k & 0xffffffff00000000ULL) | (k & 0x00000000ffffffffULL);
k ^= std::hash<size_t>()(vchSig.size()) * 0x9e3779b97f4a7c15ULL;
k ^= std::hash<size_t>()(vchPubKey.size()) * 0x517cc1b727220a95ULL;
// Mix in actual signature bytes for uniqueness
@@ -1271,7 +1275,7 @@ public:
}
};
bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode,
bool CheckSig(const vector<unsigned char>& vchSig, const vector<unsigned char>& vchPubKey, const CScript& scriptCode,
const CTransaction& txTo, unsigned int nIn, int nHashType)
{
static CSignatureCache signatureCache;
@@ -1283,18 +1287,20 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
nHashType = vchSig.back();
else if (nHashType != vchSig.back())
return false;
vchSig.pop_back();
vector<unsigned char> vchSigCopy(vchSig);
vchSigCopy.pop_back();
uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType);
if (signatureCache.Get(sighash, vchSig, vchPubKey))
if (signatureCache.Get(sighash, vchSigCopy, vchPubKey))
return true;
CKey key;
if (!key.SetPubKey(vchPubKey))
return false;
if (!key.Verify(sighash, vchSig))
if (!key.Verify(sighash, vchSigCopy))
return false;
signatureCache.Set(sighash, vchSig, vchPubKey);
@@ -1312,27 +1318,21 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
//
// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
//
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
bool Solver(const CScript& scriptPubKey, TxnOutType& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
{
// Templates
static map<txnouttype, CScript> mTemplates;
static map<TxnOutType, CScript> mTemplates;
if (mTemplates.empty())
{
// Standard tx, sender provides pubkey, receiver adds signature
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
// Triangles address tx, sender provides hash of pubkey, receiver provides signature and pubkey
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
// Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
mTemplates.insert(make_pair(TxnOutType::PubKey, CScript() << OP_PUBKEY << OP_CHECKSIG));
mTemplates.insert(make_pair(TxnOutType::PubKeyHash, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
mTemplates.insert(make_pair(TxnOutType::MultiSig, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
}
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
if (scriptPubKey.IsPayToScriptHash())
{
typeRet = TX_SCRIPTHASH;
typeRet = TxnOutType::ScriptHash;
vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
vSolutionsRet.push_back(hashBytes);
return true;
@@ -1357,7 +1357,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
{
// Found a match
typeRet = tplate.first;
if (typeRet == TX_MULTISIG)
if (typeRet == TxnOutType::MultiSig)
{
// Additional checks for TX_MULTISIG:
unsigned char m = vSolutionsRet.front()[0];
@@ -1419,7 +1419,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
}
vSolutionsRet.clear();
typeRet = TX_NONSTANDARD;
typeRet = TxnOutType::NonStandard;
return false;
}
@@ -1460,7 +1460,7 @@ bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint2
// Returns false if scriptPubKey could not be completely satisfied.
//
bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType,
CScript& scriptSigRet, txnouttype& whichTypeRet)
CScript& scriptSigRet, TxnOutType& whichTypeRet)
{
scriptSigRet.clear();
@@ -1471,12 +1471,12 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
CKeyID keyID;
switch (whichTypeRet)
{
case TX_NONSTANDARD:
case TxnOutType::NonStandard:
return false;
case TX_PUBKEY:
case TxnOutType::PubKey:
keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, keystore, hash, nHashType, scriptSigRet);
case TX_PUBKEYHASH:
case TxnOutType::PubKeyHash:
keyID = CKeyID(uint160(vSolutions[0]));
if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet))
return false;
@@ -1487,32 +1487,32 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
scriptSigRet << vch;
}
return true;
case TX_SCRIPTHASH:
case TxnOutType::ScriptHash:
return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet);
case TX_MULTISIG:
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
case TxnOutType::MultiSig:
scriptSigRet << OP_0;
return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet));
}
return false;
}
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions)
int ScriptSigArgsExpected(TxnOutType t, const std::vector<std::vector<unsigned char> >& vSolutions)
{
switch (t)
{
case TX_NONSTANDARD:
case TxnOutType::NonStandard:
return -1;
case TX_PUBKEY:
case TxnOutType::PubKey:
return 1;
case TX_PUBKEYHASH:
case TxnOutType::PubKeyHash:
return 2;
case TX_MULTISIG:
case TxnOutType::MultiSig:
if (vSolutions.size() < 1 || vSolutions[0].size() < 1)
return -1;
return vSolutions[0][0] + 1;
case TX_SCRIPTHASH:
return 1; // doesn't include args needed by the script
case TxnOutType::ScriptHash:
return 1;
}
return -1;
}
@@ -1520,11 +1520,11 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c
bool IsStandard(const CScript& scriptPubKey)
{
vector<valtype> vSolutions;
txnouttype whichType;
TxnOutType whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
if (whichType == TX_MULTISIG)
if (whichType == TxnOutType::MultiSig)
{
unsigned char m = vSolutions.front()[0];
unsigned char n = vSolutions.back()[0];
@@ -1535,7 +1535,7 @@ bool IsStandard(const CScript& scriptPubKey)
return false;
}
return whichType != TX_NONSTANDARD;
return whichType != TxnOutType::NonStandard;
}
@@ -1571,29 +1571,29 @@ bool IsMine(const CKeyStore &keystore, const CTxDestination &dest)
bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
vector<valtype> vSolutions;
txnouttype whichType;
TxnOutType whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
CKeyID keyID;
switch (whichType)
{
case TX_NONSTANDARD:
case TxnOutType::NonStandard:
return false;
case TX_PUBKEY:
case TxnOutType::PubKey:
keyID = CPubKey(vSolutions[0]).GetID();
return keystore.HaveKey(keyID);
case TX_PUBKEYHASH:
case TxnOutType::PubKeyHash:
keyID = CKeyID(uint160(vSolutions[0]));
return keystore.HaveKey(keyID);
case TX_SCRIPTHASH:
case TxnOutType::ScriptHash:
{
CScript subscript;
if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript))
return false;
return IsMine(keystore, subscript);
}
case TX_MULTISIG:
case TxnOutType::MultiSig:
{
// Only consider transactions "mine" if we own ALL the
// keys involved. multi-signature transactions that are
@@ -1610,21 +1610,21 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
vector<valtype> vSolutions;
txnouttype whichType;
TxnOutType whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
if (whichType == TX_PUBKEY)
if (whichType == TxnOutType::PubKey)
{
addressRet = CPubKey(vSolutions[0]).GetID();
return true;
}
else if (whichType == TX_PUBKEYHASH)
else if (whichType == TxnOutType::PubKeyHash)
{
addressRet = CKeyID(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
else if (whichType == TxnOutType::ScriptHash)
{
addressRet = CScriptID(uint160(vSolutions[0]));
return true;
@@ -1642,7 +1642,7 @@ public:
CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}
void Process(const CScript &script) {
txnouttype type;
TxnOutType type;
std::vector<CTxDestination> vDest;
int nRequired;
if (ExtractDestinations(script, type, vDest, nRequired)) {
@@ -1671,15 +1671,15 @@ void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey,
CAffectedKeysVisitor(keystore, vKeys).Process(scriptPubKey);
}
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector<CTxDestination>& addressRet, int& nRequiredRet)
bool ExtractDestinations(const CScript& scriptPubKey, TxnOutType& typeRet, vector<CTxDestination>& addressRet, int& nRequiredRet)
{
addressRet.clear();
typeRet = TX_NONSTANDARD;
typeRet = TxnOutType::NonStandard;
vector<valtype> vSolutions;
if (!Solver(scriptPubKey, typeRet, vSolutions))
return false;
if (typeRet == TX_MULTISIG)
if (typeRet == TxnOutType::MultiSig)
{
nRequiredRet = vSolutions.front()[0];
for (unsigned int i = 1; i < vSolutions.size()-1; i++)
@@ -1747,23 +1747,19 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa
// The checksig op will also drop the signatures from its hash.
uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType);
txnouttype whichType;
TxnOutType whichType;
if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType))
return false;
if (whichType == TX_SCRIPTHASH)
if (whichType == TxnOutType::ScriptHash)
{
// Solver returns the subscript that need to be evaluated;
// the final scriptSig is the signatures from that
// and then the serialized subscript:
CScript subscript = txin.scriptSig;
// Recompute txn hash using subscript in place of scriptPubKey:
uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType);
txnouttype subType;
TxnOutType subType;
bool fSolved =
Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH;
Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TxnOutType::ScriptHash;
// Append serialized subscript whether or not it is completely signed:
txin.scriptSig << static_cast<valtype>(subscript);
if (!fSolved) return false;
@@ -1862,23 +1858,21 @@ static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, u
}
static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn,
const txnouttype txType, const vector<valtype>& vSolutions,
const TxnOutType txType, const vector<valtype>& vSolutions,
vector<valtype>& sigs1, vector<valtype>& sigs2)
{
switch (txType)
{
case TX_NONSTANDARD:
// Don't know anything about this, assume bigger one is correct:
case TxnOutType::NonStandard:
if (sigs1.size() >= sigs2.size())
return PushAll(sigs1);
return PushAll(sigs2);
case TX_PUBKEY:
case TX_PUBKEYHASH:
// Signatures are bigger than placeholders or empty scripts:
case TxnOutType::PubKey:
case TxnOutType::PubKeyHash:
if (sigs1.empty() || sigs1[0].empty())
return PushAll(sigs2);
return PushAll(sigs1);
case TX_SCRIPTHASH:
case TxnOutType::ScriptHash:
if (sigs1.empty() || sigs1.back().empty())
return PushAll(sigs2);
else if (sigs2.empty() || sigs2.back().empty())
@@ -1889,7 +1883,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo,
valtype spk = sigs1.back();
CScript pubKey2(spk.begin(), spk.end());
txnouttype txType2;
TxnOutType txType2;
vector<vector<unsigned char> > vSolutions2;
Solver(pubKey2, txType2, vSolutions2);
sigs1.pop_back();
@@ -1898,7 +1892,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo,
result << spk;
return result;
}
case TX_MULTISIG:
case TxnOutType::MultiSig:
return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2);
}
@@ -1908,7 +1902,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo,
CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn,
const CScript& scriptSig1, const CScript& scriptSig2)
{
txnouttype txType;
TxnOutType txType;
vector<vector<unsigned char> > vSolutions;
Solver(scriptPubKey, txType, vSolutions);
+20 -21
View File
@@ -16,7 +16,7 @@
#include "keystore.h"
#include "bignum.h"
typedef std::vector<unsigned char> valtype;
using valtype = std::vector<unsigned char>;
class CTransaction;
@@ -30,14 +30,13 @@ enum
};
enum txnouttype
enum class TxnOutType
{
TX_NONSTANDARD,
// 'standard' transaction types:
TX_PUBKEY,
TX_PUBKEYHASH,
TX_SCRIPTHASH,
TX_MULTISIG,
NonStandard,
PubKey,
PubKeyHash,
ScriptHash,
MultiSig,
};
class CNoDestination {
@@ -52,9 +51,9 @@ public:
* * CScriptID: TX_SCRIPTHASH destination
* A CTxDestination is the internal data type encoded in a CTrianglesAddress
*/
typedef std::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
using CTxDestination = std::variant<CNoDestination, CKeyID, CScriptID>;
const char* GetTxnOutputType(txnouttype t);
const char* GetTxnOutputType(TxnOutType t);
/** Script opcodes */
enum opcodetype
@@ -267,7 +266,7 @@ protected:
public:
CScript() { }
CScript(const CScript& b) : std::vector<unsigned char>(b.begin(), b.end()) { }
CScript(const CScript& b) = default;
CScript(const_iterator pbegin, const_iterator pend) : std::vector<unsigned char>(pbegin, pend) { }
#ifndef _MSC_VER
CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector<unsigned char>(pbegin, pend) { }
@@ -590,19 +589,19 @@ public:
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
bool IsStandard(const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CTxDestination &dest);
bool Solver(const CScript& scriptPubKey, TxnOutType& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
int ScriptSigArgsExpected(TxnOutType t, const std::vector<std::vector<unsigned char> >& vSolutions);
[[nodiscard]] bool IsStandard(const CScript& scriptPubKey);
[[nodiscard]] bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
[[nodiscard]] bool IsMine(const CKeyStore& keystore, const CTxDestination &dest);
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
bool ExtractDestinations(const CScript& scriptPubKey, TxnOutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
[[nodiscard]] bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
[[nodiscard]] bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
[[nodiscard]] bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
int nHashType);
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
[[nodiscard]] bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders,
// combine them intelligently and return the result.
+32 -35
View File
@@ -17,7 +17,7 @@
#include <cstring>
#include <cstdio>
#include <boost/type_traits/is_fundamental.hpp>
#include <type_traits>
#include <tuple>
#include "allocators.h"
@@ -59,12 +59,11 @@ enum
unsigned int GetSerializeSize(int nType, int nVersion) const \
{ \
CSerActionGetSerializeSize ser_action; \
const bool fGetSize = true; \
const bool fWrite = false; \
const bool fRead = false; \
[[maybe_unused]] const bool fGetSize = true; \
[[maybe_unused]] const bool fWrite = false; \
[[maybe_unused]] const bool fRead = false; \
unsigned int nSerSize = 0; \
ser_streamplaceholder s; \
assert(fGetSize||fWrite||fRead); /* suppress warning */ \
s.nType = nType; \
s.nVersion = nVersion; \
{statements} \
@@ -74,22 +73,20 @@ enum
void Serialize(Stream& s, int nType, int nVersion) const \
{ \
CSerActionSerialize ser_action; \
const bool fGetSize = false; \
const bool fWrite = true; \
const bool fRead = false; \
unsigned int nSerSize = 0; \
assert(fGetSize||fWrite||fRead); /* suppress warning */ \
[[maybe_unused]] const bool fGetSize = false; \
[[maybe_unused]] const bool fWrite = true; \
[[maybe_unused]] const bool fRead = false; \
[[maybe_unused]] unsigned int nSerSize = 0; \
{statements} \
} \
template<typename Stream> \
void Unserialize(Stream& s, int nType, int nVersion) \
{ \
CSerActionUnserialize ser_action; \
const bool fGetSize = false; \
const bool fWrite = false; \
const bool fRead = true; \
unsigned int nSerSize = 0; \
assert(fGetSize||fWrite||fRead); /* suppress warning */ \
[[maybe_unused]] const bool fGetSize = false; \
[[maybe_unused]] const bool fWrite = false; \
[[maybe_unused]] const bool fRead = true; \
[[maybe_unused]] unsigned int nSerSize = 0; \
{statements} \
}
@@ -288,14 +285,14 @@ template<typename Stream, typename C> void Serialize(Stream& os, const std::basi
template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str, int, int=0);
// vector
template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&);
template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&);
template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const std::true_type&);
template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const std::false_type&);
template<typename T, typename A> inline unsigned int GetSerializeSize(const std::vector<T, A>& v, int nType, int nVersion);
template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&);
template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&);
template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const std::true_type&);
template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const std::false_type&);
template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v, int nType, int nVersion);
template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&);
template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&);
template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const std::true_type&);
template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const std::false_type&);
template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersion);
// others derived from vector
@@ -392,13 +389,13 @@ void Unserialize(Stream& is, std::basic_string<C>& str, int, int)
// vector
//
template<typename T, typename A>
unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&)
unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const std::true_type&)
{
return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T));
}
template<typename T, typename A>
unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&)
unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const std::false_type&)
{
unsigned int nSize = GetSizeOfCompactSize(v.size());
for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi)
@@ -409,12 +406,12 @@ unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nV
template<typename T, typename A>
inline unsigned int GetSerializeSize(const std::vector<T, A>& v, int nType, int nVersion)
{
return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental<T>());
return GetSerializeSize_impl(v, nType, nVersion, std::is_fundamental<T>{});
}
template<typename Stream, typename T, typename A>
void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&)
void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const std::true_type&)
{
WriteCompactSize(os, v.size());
if (!v.empty())
@@ -422,7 +419,7 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVers
}
template<typename Stream, typename T, typename A>
void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&)
void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const std::false_type&)
{
WriteCompactSize(os, v.size());
for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi)
@@ -432,12 +429,12 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVers
template<typename Stream, typename T, typename A>
inline void Serialize(Stream& os, const std::vector<T, A>& v, int nType, int nVersion)
{
Serialize_impl(os, v, nType, nVersion, boost::is_fundamental<T>());
Serialize_impl(os, v, nType, nVersion, std::is_fundamental<T>{});
}
template<typename Stream, typename T, typename A>
void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&)
void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const std::true_type&)
{
// Limit size per read so bogus size value won't cause out of memory
v.clear();
@@ -453,7 +450,7 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion,
}
template<typename Stream, typename T, typename A>
void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&)
void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const std::false_type&)
{
v.clear();
unsigned int nSize = ReadCompactSize(is);
@@ -473,7 +470,7 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion,
template<typename Stream, typename T, typename A>
inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersion)
{
Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental<T>());
Unserialize_impl(is, v, nType, nVersion, std::is_fundamental<T>{});
}
@@ -705,7 +702,7 @@ struct ser_streamplaceholder
typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData;
using CSerializeData = std::vector<char, zero_after_free_allocator<char>>;
/** Double ended buffer combining vector and stream-like interfaces.
*
@@ -1060,18 +1057,18 @@ public:
void fclose()
{
if (file != NULL && file != stdin && file != stdout && file != stderr)
if (file != nullptr && file != stdin && file != stdout && file != stderr)
::fclose(file);
file = NULL;
file = nullptr;
}
FILE* release() { FILE* ret = file; file = NULL; return ret; }
FILE* release() { FILE* ret = file; file = nullptr; return ret; }
operator FILE*() { return file; }
FILE* operator->() { return file; }
FILE& operator*() { return *file; }
FILE** operator&() { return &file; }
FILE* operator=(FILE* pnew) { return file = pnew; }
bool operator!() { return (file == NULL); }
bool operator!() { return (file == nullptr); }
//
+20 -22
View File
@@ -47,8 +47,6 @@ Notes:
#include <openssl/hmac.h>
#include <string>
#include <boost/algorithm/string/predicate.hpp>
#include "base58.h"
#include "crypto_ecdh.h"
@@ -99,7 +97,7 @@ uint32_t nPeerIdCounter = 1;
CCriticalSection cs_smsg;
CCriticalSection cs_smsgDB;
rocksdb::DB *smsgDB = NULL;
rocksdb::DB *smsgDB = nullptr;
namespace fs = std::filesystem;
@@ -292,12 +290,12 @@ bool SecureMsgAllDigits(const std::string& value)
bool SecureMsgParseBucketFilename(const std::string& fileName, int64_t& bucket, uint32_t& fileIndex, bool& fWalletLocked)
{
if (!boost::algorithm::ends_with(fileName, ".dat"))
if (!fileName.ends_with(".dat"))
return false;
std::string baseName = fileName.substr(0, fileName.size() - 4);
fWalletLocked = false;
if (boost::algorithm::ends_with(baseName, "_wl"))
if (baseName.ends_with("_wl"))
{
fWalletLocked = true;
baseName.erase(baseName.size() - 3);
@@ -366,7 +364,7 @@ void SecureMsgGetBucketFiles(const fs::path& pathSmsgDir, int64_t bucket, bool f
|| fFileWalletLocked != fWalletLocked)
continue;
bucketFiles.push_back(std::make_pair(fileIndex, (*itd).path()));
bucketFiles.push_back({fileIndex, (*itd).path()});
};
std::sort(bucketFiles.begin(), bucketFiles.end(),
@@ -471,7 +469,7 @@ bool SecMsgCrypter::Encrypt(unsigned char* chPlaintext, uint32_t nPlain, std::ve
bool fOk = true;
if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, &chKey[0], &chIV[0]);
if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, &chKey[0], &chIV[0]);
if (fOk) fOk = EVP_EncryptUpdate(ctx, &vchCiphertext[0], &nCLen, chPlaintext, nLen);
if (fOk) fOk = EVP_EncryptFinal_ex(ctx, (&vchCiphertext[0])+nCLen, &nFLen);
EVP_CIPHER_CTX_free(ctx);
@@ -500,7 +498,7 @@ bool SecMsgCrypter::Decrypt(unsigned char* chCiphertext, uint32_t nCipher, std::
bool fOk = true;
if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, &chKey[0], &chIV[0]);
if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, &chKey[0], &chIV[0]);
if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &chCiphertext[0], nCipher);
if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0])+nPLen, &nFLen);
EVP_CIPHER_CTX_free(ctx);
@@ -626,7 +624,7 @@ bool SecMsgDB::TxnCommit()
writeOptions.sync = true;
rocksdb::Status status = pdb->Write(writeOptions, activeBatch);
delete activeBatch;
activeBatch = NULL;
activeBatch = nullptr;
pendingBatch.clear();
if (!status.ok())
@@ -641,7 +639,7 @@ bool SecMsgDB::TxnCommit()
bool SecMsgDB::TxnAbort()
{
delete activeBatch;
activeBatch = NULL;
activeBatch = nullptr;
pendingBatch.clear();
return true;
};
@@ -1344,7 +1342,7 @@ int SecureMsgReadIni()
continue;
if (!(pName = strtok(cLine, "="))
|| !(pValue = strtok(NULL, "=")))
|| !(pValue = strtok(nullptr, "=")))
continue;
if (strcmp(pName, "newAddressRecv") == 0)
@@ -1478,8 +1476,8 @@ bool SecureMsgStart(bool fDontStart, bool fScanChain)
};
// -- start threads
if (!NewThread(ThreadSecureMsg, NULL)
|| !NewThread(ThreadSecureMsgPow, NULL))
if (!NewThread(ThreadSecureMsg, nullptr)
|| !NewThread(ThreadSecureMsgPow, nullptr))
{
printf("SecureMsg could not start threads, secure messaging disabled.\n");
fSecMsgEnabled = false;
@@ -1509,7 +1507,7 @@ bool SecureMsgShutdown()
{
LOCK(cs_smsgDB);
delete smsgDB;
smsgDB = NULL;
smsgDB = nullptr;
};
// -- main program will wait 5 seconds for threads to terminate.
@@ -1553,8 +1551,8 @@ bool SecureMsgEnable()
}; // LOCK(cs_smsg);
// -- start threads
if (!NewThread(ThreadSecureMsg, NULL)
|| !NewThread(ThreadSecureMsgPow, NULL))
if (!NewThread(ThreadSecureMsg, nullptr)
|| !NewThread(ThreadSecureMsgPow, nullptr))
{
printf("SecureMsgEnable could not start threads, secure messaging disabled.\n");
fSecMsgEnabled = false;
@@ -1624,7 +1622,7 @@ bool SecureMsgDisable()
{
LOCK(cs_smsgDB);
delete smsgDB;
smsgDB = NULL;
smsgDB = nullptr;
};
@@ -2455,7 +2453,7 @@ bool SecureMsgScanBlockChain()
if (lockMain)
{
CBlockIndex *pindexScan = pindexGenesisBlock;
if (pindexScan == NULL)
if (pindexScan == nullptr)
{
printf("Error: pindexGenesisBlock not set.\n");
return false;
@@ -3521,7 +3519,7 @@ int SecureMsgValidate(unsigned char *pHeader, unsigned char *pPayload, uint32_t
HMAC_CTX *ctx = HMAC_CTX_new();
unsigned int nBytes;
if (!HMAC_Init_ex(ctx, &civ[0], 32, EVP_sha256(), NULL)
if (!HMAC_Init_ex(ctx, &civ[0], 32, EVP_sha256(), nullptr)
|| !HMAC_Update(ctx, (unsigned char*) pHeader+4, SMSG_HDR_LEN-4)
|| !HMAC_Update(ctx, (unsigned char*) pPayload, nPayload)
|| !HMAC_Update(ctx, pPayload, nPayload)
@@ -3598,7 +3596,7 @@ int SecureMsgSetHash(unsigned char *pHeader, unsigned char *pPayload, uint32_t n
memcpy(civ+i, &nonse, 4);
unsigned int nBytes;
if (!HMAC_Init_ex(ctx, &civ[0], 32, EVP_sha256(), NULL)
if (!HMAC_Init_ex(ctx, &civ[0], 32, EVP_sha256(), nullptr)
|| !HMAC_Update(ctx, (unsigned char*) pHeader+4, SMSG_HDR_LEN-4)
|| !HMAC_Update(ctx, (unsigned char*) pPayload, nPayload)
|| !HMAC_Update(ctx, pPayload, nPayload)
@@ -3923,7 +3921,7 @@ int SecureMsgEncrypt(SecureMessage& smsg, std::string& addressFrom, std::string&
unsigned int nBytes = 32;
HMAC_CTX *ctx = HMAC_CTX_new();
if (!HMAC_Init_ex(ctx, &key_m[0], 32, EVP_sha256(), NULL)
if (!HMAC_Init_ex(ctx, &key_m[0], 32, EVP_sha256(), nullptr)
|| !HMAC_Update(ctx, (unsigned char*) &smsg.timestamp, sizeof(smsg.timestamp))
|| !HMAC_Update(ctx, &vchCiphertext[0], vchCiphertext.size())
|| !HMAC_Final(ctx, smsg.mac, &nBytes)
@@ -4233,7 +4231,7 @@ int SecureMsgDecrypt(bool fTestOnly, std::string& address, unsigned char *pHeade
unsigned int nBytes = 32;
HMAC_CTX *ctx = HMAC_CTX_new();
if (!HMAC_Init_ex(ctx, &key_m[0], 32, EVP_sha256(), NULL)
if (!HMAC_Init_ex(ctx, &key_m[0], 32, EVP_sha256(), nullptr)
|| !HMAC_Update(ctx, (unsigned char*) &psmsg->timestamp, sizeof(psmsg->timestamp))
|| !HMAC_Update(ctx, pPayload, nPayload)
|| !HMAC_Final(ctx, MAC, &nBytes)
+3 -3
View File
@@ -74,14 +74,14 @@ public:
SecureMessage()
{
nPayload = 0;
pPayload = NULL;
pPayload = nullptr;
};
~SecureMessage()
{
if (pPayload)
delete[] pPayload;
pPayload = NULL;
pPayload = nullptr;
};
unsigned char hash[4];
@@ -294,7 +294,7 @@ class SecMsgDB
public:
SecMsgDB()
{
activeBatch = NULL;
activeBatch = nullptr;
};
~SecMsgDB()
+4 -4
View File
@@ -46,7 +46,7 @@ private:
int sourceLine;
};
typedef std::vector< std::pair<void*, CLockLocation> > LockStack;
using LockStack = std::vector<std::pair<void*, CLockLocation>>;
static std::mutex dd_mutex;
static std::map<std::pair<void*, void*>, LockStack> lockorders;
@@ -80,18 +80,18 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry)
if (fDebug) printf("Locking: %s\n", locklocation.ToString().c_str());
dd_mutex.lock();
(*lockstack).push_back(std::make_pair(c, locklocation));
(*lockstack).push_back({c, locklocation});
if (!fTry) {
for (const auto& i : (*lockstack)) {
if (i.first == c) break;
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
std::pair<void*, void*> p1 = {i.first, c};
if (lockorders.count(p1))
continue;
lockorders[p1] = (*lockstack);
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
std::pair<void*, void*> p2 = {c, i.first};
if (lockorders.count(p2))
{
potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]);
+5 -5
View File
@@ -9,10 +9,10 @@
#include <condition_variable>
/** Recursive mutex: supports recursive locking, but no waiting */
typedef std::recursive_mutex CCriticalSection;
using CCriticalSection = std::recursive_mutex;
/** Plain mutex: supports waiting but not recursive locking */
typedef std::mutex CWaitableCriticalSection;
using CWaitableCriticalSection = std::mutex;
#ifdef DEBUG_LOCKORDER
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
@@ -26,7 +26,7 @@ void static inline LeaveCritical() {}
void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
#endif
/** Wrapper around boost::unique_lock<Mutex> */
/** Wrapper around std::unique_lock<Mutex> */
template<typename Mutex>
class CMutexLock
{
@@ -182,11 +182,11 @@ public:
grant.Release();
grant.sem = sem;
grant.fHaveGrant = fHaveGrant;
sem = NULL;
sem = nullptr;
fHaveGrant = false;
}
CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {}
CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {}
CSemaphoreGrant(CSemaphore &sema, bool fTry = false) : sem(&sema), fHaveGrant(false) {
if (fTry)
+664
View File
@@ -0,0 +1,664 @@
// Copyright (c) 2026 The Triangles developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "syncmanager.h"
#include "bignum.h"
#include "checkpoints.h"
#include "main.h"
#include "net.h"
#include "util.h"
#include <algorithm>
#include <map>
struct CSyncManager::HeaderNode
{
CBlock header;
int nHeight;
uint256 nChainTrust;
bool fRequested;
int64_t nLastRequestTime;
int64_t nFirstRequestTime;
int64_t nInsertTime;
};
namespace
{
static const unsigned int MAX_HEADER_SYNC_CACHE = 15000;
static const size_t HEADER_REDUNDANT_PEER_THRESHOLD = 4;
static const int64_t HEADER_REQUEST_TIMEOUT_MICROS = 60 * 1000000;
static const int64_t HEADER_REDUNDANT_REQUEST_MICROS = 5 * 1000000;
static const int64_t HEADER_SYNC_TTL_MICROS = 15 * 60 * 1000000;
std::map<uint256, CSyncManager::HeaderNode> mapHeaders;
uint256 hashBestHeader = 0;
int64_t nLastNewHeaderTime = 0;
}
CSyncManager g_syncManager;
bool CSyncManager::HaveHeader(const uint256& hash) const
{
return mapHeaders.count(hash) != 0;
}
uint256 CSyncManager::GetBestHeader() const
{
return hashBestHeader;
}
std::size_t CSyncManager::GetHeaderCount() const
{
return mapHeaders.size();
}
uint256 CSyncManager::GetHeaderTrust(unsigned int nBits) const
{
CBigNum bnTarget;
bnTarget.SetCompact(nBits);
if (bnTarget <= 0)
return 0;
return ((CBigNum(1) << 256) / (bnTarget + 1)).getuint256();
}
bool CSyncManager::GetKnownHeaderState(const uint256& hash, int& nHeight, uint256& nChainTrust) const
{
std::map<uint256, CBlockIndex*>::const_iterator miBlock = mapBlockIndex.find(hash);
if (miBlock != mapBlockIndex.end())
{
nHeight = miBlock->second->nHeight;
nChainTrust = miBlock->second->nChainTrust;
return true;
}
std::map<uint256, HeaderNode>::const_iterator miHeader = mapHeaders.find(hash);
if (miHeader != mapHeaders.end())
{
nHeight = miHeader->second.nHeight;
nChainTrust = miHeader->second.nChainTrust;
return true;
}
return false;
}
bool CSyncManager::GetPrevHash(const uint256& hash, uint256& hashPrev) const
{
std::map<uint256, HeaderNode>::const_iterator miHeader = mapHeaders.find(hash);
if (miHeader != mapHeaders.end())
{
hashPrev = miHeader->second.header.hashPrevBlock;
return true;
}
std::map<uint256, CBlockIndex*>::const_iterator miBlock = mapBlockIndex.find(hash);
if (miBlock != mapBlockIndex.end() && miBlock->second->pprev)
{
hashPrev = miBlock->second->pprev->GetBlockHash();
return true;
}
return false;
}
void CSyncManager::RecomputeBestHeader()
{
hashBestHeader = 0;
uint256 nBestTrust = 0;
for (std::map<uint256, HeaderNode>::const_iterator it = mapHeaders.begin(); it != mapHeaders.end(); ++it)
{
if (hashBestHeader == 0 || it->second.nChainTrust > nBestTrust)
{
hashBestHeader = it->first;
nBestTrust = it->second.nChainTrust;
}
}
}
void CSyncManager::PruneHeaders()
{
const int64_t nNow = GetTime() * 1000000;
if (mapHeaders.size() > MAX_HEADER_SYNC_CACHE / 2)
{
unsigned int nEvicted = 0;
for (std::map<uint256, HeaderNode>::iterator it = mapHeaders.begin(); it != mapHeaders.end(); )
{
if (nNow - it->second.nInsertTime >= HEADER_SYNC_TTL_MICROS)
{
it = mapHeaders.erase(it);
++nEvicted;
}
else
++it;
}
if (nEvicted > 0)
{
printf("IBD-DIAG: TTL-evicted %u stale sync headers, %u remain\n",
nEvicted, (unsigned int)mapHeaders.size());
RecomputeBestHeader();
}
}
if (mapHeaders.size() > MAX_HEADER_SYNC_CACHE)
{
printf("IBD-DIAG: sync header cache exceeded %u entries, evicting oldest\n", MAX_HEADER_SYNC_CACHE);
while (mapHeaders.size() > MAX_HEADER_SYNC_CACHE * 3 / 4)
{
std::map<uint256, HeaderNode>::iterator oldest = mapHeaders.begin();
for (std::map<uint256, HeaderNode>::iterator it = mapHeaders.begin(); it != mapHeaders.end(); ++it)
{
if (it->second.nInsertTime < oldest->second.nInsertTime)
oldest = it;
}
mapHeaders.erase(oldest);
}
RecomputeBestHeader();
}
}
bool CSyncManager::AddHeaderNode(const CBlock& header, const uint256& hashHeader)
{
if (mapBlockIndex.count(hashHeader) || mapHeaders.count(hashHeader))
return true;
if (!header.vtx.empty())
{
printf("IBD-DIAG: header rejected (has vtx) hash=%s\n", hashHeader.ToString().substr(0,20).c_str());
return false;
}
if (header.GetBlockTime() > GetTime() + 15 * 60)
{
printf("IBD-DIAG: header rejected (future time) hash=%s time=%u\n",
hashHeader.ToString().substr(0,20).c_str(), header.nTime);
return false;
}
int nPrevHeight = -1;
uint256 nPrevChainTrust = 0;
if (!GetKnownHeaderState(header.hashPrevBlock, nPrevHeight, nPrevChainTrust))
{
printf("IBD-DIAG: header rejected (prev unknown) hash=%s prevHash=%s\n",
hashHeader.ToString().substr(0,20).c_str(),
header.hashPrevBlock.ToString().substr(0,20).c_str());
return false;
}
const int nHeight = nPrevHeight + 1;
if (nHeight <= CUTOFF_POW_BLOCK && !CheckProofOfWork(hashHeader, header.nBits))
{
printf("IBD-DIAG: header PoW FAILED at height %d hash=%s nBits=%08x prevHash=%s\n",
nHeight, hashHeader.ToString().substr(0,20).c_str(), header.nBits,
header.hashPrevBlock.ToString().substr(0,20).c_str());
return false;
}
HeaderNode node;
node.header = header;
node.nHeight = nHeight;
node.nChainTrust = nPrevChainTrust + GetHeaderTrust(header.nBits);
node.fRequested = false;
node.nLastRequestTime = 0;
node.nFirstRequestTime = 0;
node.nInsertTime = GetTime() * 1000000;
mapHeaders.insert({hashHeader, node});
if (hashBestHeader == 0 || node.nChainTrust > mapHeaders[hashBestHeader].nChainTrust)
hashBestHeader = hashHeader;
PruneHeaders();
return true;
}
std::vector<uint256> CSyncManager::GetDownloadPath(uint256 hashTip) const
{
std::vector<uint256> vPath;
while (hashTip != 0 && !mapBlockIndex.count(hashTip))
{
std::map<uint256, HeaderNode>::const_iterator mi = mapHeaders.find(hashTip);
if (mi == mapHeaders.end())
break;
vPath.push_back(hashTip);
hashTip = mi->second.header.hashPrevBlock;
}
std::reverse(vPath.begin(), vPath.end());
return vPath;
}
unsigned int CSyncManager::CountInFlight() const
{
const int64_t nNow = GetTime() * 1000000;
unsigned int nInFlight = 0;
for (std::map<uint256, HeaderNode>::const_iterator it = mapHeaders.begin(); it != mapHeaders.end(); ++it)
{
if (it->second.fRequested && nNow - it->second.nLastRequestTime < HEADER_REQUEST_TIMEOUT_MICROS)
++nInFlight;
}
return nInFlight;
}
unsigned int CSyncManager::GetPlannerDepth() const
{
if (hashBestHeader == 0)
return 0;
return (unsigned int)GetDownloadPath(hashBestHeader).size();
}
int CSyncManager::GetPlannerHeight() const
{
if (hashBestHeader == 0)
return pindexBest ? pindexBest->nHeight : -1;
std::map<uint256, HeaderNode>::const_iterator mi = mapHeaders.find(hashBestHeader);
if (mi == mapHeaders.end())
return pindexBest ? pindexBest->nHeight : -1;
return mi->second.nHeight;
}
int64_t CSyncManager::GetRequestTime(const uint256& hashBlock) const
{
std::map<uint256, HeaderNode>::const_iterator mi = mapHeaders.find(hashBlock);
if (mi == mapHeaders.end())
return 0;
return mi->second.nFirstRequestTime;
}
void CSyncManager::BlockAccepted(const uint256& hashBlock)
{
std::map<uint256, HeaderNode>::iterator mi = mapHeaders.find(hashBlock);
if (mi == mapHeaders.end())
return;
mapHeaders.erase(mi);
if (hashBestHeader == hashBlock)
RecomputeBestHeader();
}
void CSyncManager::ContinueHeaders(CNode* pfrom, const uint256& hashTip)
{
if (!pfrom || hashTip == 0)
return;
std::vector<uint256> vHave;
uint256 hashWalk = hashTip;
int nStep = 1;
while (hashWalk != 0)
{
vHave.push_back(hashWalk);
for (int i = 0; i < nStep && hashWalk != 0; ++i)
{
uint256 hashPrev = 0;
if (!GetPrevHash(hashWalk, hashPrev))
hashWalk = 0;
else
hashWalk = hashPrev;
}
if (vHave.size() > 10)
nStep *= 2;
}
vHave.push_back(!fTestNet ? hashGenesisBlockOfficial : hashGenesisBlockTestNet);
pfrom->PushMessage("getheaders", CBlockLocator(vHave), uint256(0));
}
bool CSyncManager::RequestRefill(CNode* pfrom, uint256 hashTip, int64_t nMinIntervalSeconds, const char* pszReason)
{
if (!pfrom || pfrom->fClient || pfrom->nVersion == 0 || !IsInitialBlockDownload())
return false;
const int64_t nNowSec = GetTime();
if (nMinIntervalSeconds > 0 &&
nNowSec - pfrom->nLastIbdHeaderRequest < nMinIntervalSeconds)
return false;
uint256 hashLocatorTip = hashTip;
if (hashLocatorTip == 0 ||
(!mapBlockIndex.count(hashLocatorTip) && !mapHeaders.count(hashLocatorTip)))
{
hashLocatorTip = hashBestHeader;
}
if (hashLocatorTip != 0 && (!pindexBest || hashLocatorTip != pindexBest->GetBlockHash()))
{
ContinueHeaders(pfrom, hashLocatorTip);
}
else
{
if (!pindexBest)
return false;
pfrom->pindexLastGetHeadersBegin = NULL;
pfrom->PushGetHeaders(pindexBest, uint256(0));
hashLocatorTip = pindexBest->GetBlockHash();
}
pfrom->nLastIbdHeaderRequest = nNowSec;
printf("IBD-DIAG: %s getheaders to peer=%s locator=%s plannerDepth=%u inflight=%u\n",
pszReason, pfrom->addr.ToString().c_str(),
hashLocatorTip.ToString().substr(0,20).c_str(),
GetPlannerDepth(), CountInFlight());
return true;
}
unsigned int CSyncManager::RequestRefillAllPeers(uint256 hashTip, int64_t nMinIntervalSeconds, const char* pszReason)
{
std::vector<CNode*> vEligiblePeers;
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
{
if (!pnode->fClient && pnode->nVersion != 0 && !pnode->fDisconnect)
vEligiblePeers.push_back(pnode);
}
}
unsigned int nRequested = 0;
for (CNode* pnode : vEligiblePeers)
{
if (RequestRefill(pnode, hashTip, nMinIntervalSeconds, pszReason))
++nRequested;
}
return nRequested;
}
unsigned int CSyncManager::QueueBlocksParallel(unsigned int nWindow)
{
if (hashBestHeader == 0)
return 0;
const std::vector<uint256> vPath = GetDownloadPath(hashBestHeader);
if (vPath.empty())
return 0;
std::vector<CNode*> vEligiblePeers;
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
{
if (!pnode->fClient && pnode->nVersion != 0 && !pnode->fDisconnect)
vEligiblePeers.push_back(pnode);
}
}
if (vEligiblePeers.empty())
return 0;
const int64_t nNow = GetTime() * 1000000;
unsigned int nInFlight = CountInFlight();
unsigned int nQueued = 0;
unsigned int nPeerIndex = 0;
std::sort(vEligiblePeers.begin(), vEligiblePeers.end(),
[](const CNode* a, const CNode* b) {
return a->nBlocksDelivered > b->nBlocksDelivered;
});
std::vector<CNode*> vWeightedPeers;
for (size_t i = 0; i < vEligiblePeers.size(); i++)
{
int nWeight = (i == 0) ? 3 : (i == 1) ? 2 : 1;
for (int w = 0; w < nWeight; w++)
vWeightedPeers.push_back(vEligiblePeers[i]);
}
int64_t nAdaptiveTimeout = HEADER_REQUEST_TIMEOUT_MICROS;
{
int64_t nTotalLatency = 0;
int nPeersWithLatency = 0;
for (const CNode* pnode : vEligiblePeers)
{
if (pnode->nAvgBlockLatencyUs > 0)
{
nTotalLatency += pnode->nAvgBlockLatencyUs;
++nPeersWithLatency;
}
}
if (nPeersWithLatency > 0)
{
int64_t nAvgLatency = nTotalLatency / nPeersWithLatency;
nAdaptiveTimeout = std::max((int64_t)(10 * 1000000),
std::min((int64_t)(60 * 1000000), nAvgLatency * 5));
}
}
for (std::vector<uint256>::const_iterator it = vPath.begin(); it != vPath.end(); ++it)
{
if (nInFlight + nQueued >= nWindow)
break;
std::map<uint256, HeaderNode>::iterator mi = mapHeaders.find(*it);
if (mi == mapHeaders.end())
continue;
bool fNeedsRequest = false;
if (!mi->second.fRequested)
fNeedsRequest = true;
else if (nNow - mi->second.nLastRequestTime >= nAdaptiveTimeout)
fNeedsRequest = true;
else if (nNow - mi->second.nLastRequestTime >= HEADER_REDUNDANT_REQUEST_MICROS)
fNeedsRequest = true;
if (!fNeedsRequest)
continue;
CNode* pnode = vWeightedPeers[nPeerIndex % vWeightedPeers.size()];
pnode->AskFor(CInv(MSG_BLOCK, *it));
if (IsInitialBlockDownload() &&
vWeightedPeers.size() >= 2 &&
vWeightedPeers.size() < HEADER_REDUNDANT_PEER_THRESHOLD &&
!mi->second.fRequested)
{
CNode* pnode2 = vWeightedPeers[(nPeerIndex + 1) % vWeightedPeers.size()];
if (pnode2 != pnode)
pnode2->AskFor(CInv(MSG_BLOCK, *it));
}
if (!mi->second.fRequested || nNow - mi->second.nLastRequestTime >= HEADER_REQUEST_TIMEOUT_MICROS)
{
if (!mi->second.fRequested)
mi->second.nFirstRequestTime = nNow;
mi->second.fRequested = true;
mi->second.nLastRequestTime = nNow;
}
++nQueued;
++nPeerIndex;
}
if (nQueued > 0)
printf("IBD-DIAG: sync manager queued %u blocks across %zu peers (window=%u, inflight=%u)\n",
nQueued, vEligiblePeers.size(), nWindow, nInFlight);
return nQueued;
}
bool CSyncManager::ProcessHeaders(CNode* pfrom, const std::vector<CBlock>& vHeaders)
{
if (vHeaders.size() > 2000)
{
pfrom->Misbehaving(20);
return error("message headers size() = %" PRIszu "", vHeaders.size());
}
uint256 hashChainTip = 0;
int nNewHeaders = 0;
for (const CBlock& header : vHeaders)
{
if (!header.vtx.empty())
{
pfrom->Misbehaving(20);
return error("headers message includes transactions");
}
const uint256 hashHeader = header.GetHash();
if (mapBlockIndex.count(hashHeader) || mapHeaders.count(hashHeader))
{
hashChainTip = hashHeader;
continue;
}
if (hashChainTip != 0)
{
if (header.hashPrevBlock != hashChainTip)
{
pfrom->Misbehaving(20);
return error("non-continuous headers sequence");
}
}
else
{
std::map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(header.hashPrevBlock);
if (miPrev == mapBlockIndex.end() && !mapHeaders.count(header.hashPrevBlock))
break;
}
if (!AddHeaderNode(header, hashHeader))
{
pfrom->Misbehaving(20);
return error("invalid header sequence");
}
hashChainTip = hashHeader;
nNewHeaders++;
}
int nRequested = 0;
if (hashBestHeader != 0)
nRequested = QueueBlocksParallel(HEADER_DOWNLOAD_WINDOW);
if (nNewHeaders > 0)
nLastNewHeaderTime = GetTime();
if (nNewHeaders > 0 || nRequested > 0)
printf("IBD-DIAG: accepted %d new headers, queued %d blocks from %zu headers (peer=%s bestHeader=%s)\n",
nNewHeaders, nRequested, vHeaders.size(), pfrom->addr.ToString().c_str(),
hashBestHeader.ToString().substr(0,20).c_str());
if (vHeaders.size() >= 2000)
{
if (IsInitialBlockDownload() && hashChainTip != 0)
ContinueHeaders(pfrom, hashChainTip);
else
pfrom->PushGetBlocks(pindexBest, uint256(0));
}
else if (IsInitialBlockDownload() && nNewHeaders > 0 && hashChainTip != 0)
{
ContinueHeaders(pfrom, hashChainTip);
}
else if (IsInitialBlockDownload())
{
const unsigned int nPlannerDepth = GetPlannerDepth();
if (nPlannerDepth <= HEADER_SYNC_LOW_WATER)
RequestRefill(
pfrom, (hashChainTip != 0) ? hashChainTip : hashBestHeader,
HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS,
(nPlannerDepth == 0) ? "headers planner empty" : "headers planner low-water");
}
return true;
}
void CSyncManager::TrackBlockDelivery(CNode* pfrom, const uint256& hashBlock)
{
if (!pfrom)
return;
pfrom->nBlocksDelivered++;
if (nBestHeight > pfrom->nBestKnownHeight)
pfrom->nBestKnownHeight = nBestHeight;
int64_t nRequestTime = GetRequestTime(hashBlock);
if (nRequestTime > 0)
{
int64_t nLatency = GetTime() * 1000000 - nRequestTime;
if (nLatency > 0)
{
if (pfrom->nAvgBlockLatencyUs == 0)
pfrom->nAvgBlockLatencyUs = nLatency;
else
pfrom->nAvgBlockLatencyUs = (pfrom->nAvgBlockLatencyUs * 7 + nLatency) / 8;
}
}
}
void CSyncManager::Tick(CNode* pto, int nHighestInvWalk, const uint256& hashHighestInvWalk)
{
if (!pto || pto->fClient || pto->nVersion == 0 || !IsInitialBlockDownload())
return;
const int64_t nNowSec = GetTime();
const unsigned int nPlannerDepth = GetPlannerDepth();
const unsigned int nInFlight = CountInFlight();
static int64_t nLastHeaderPlannerControl = 0;
static int64_t nLastHeaderWatchdog = 0;
static int64_t nLastBlockPlannerControl = 0;
if (nLastNewHeaderTime == 0)
nLastNewHeaderTime = nNowSec;
if (nNowSec - nLastHeaderPlannerControl >= HEADER_SYNC_CONTROL_INTERVAL_SECONDS &&
nPlannerDepth < HEADER_SYNC_LOW_WATER &&
nInFlight < HEADER_SYNC_TARGET_INFLIGHT)
{
const unsigned int nRefilled = RequestRefillAllPeers(
hashBestHeader, HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS,
"control-loop");
if (nRefilled > 0)
printf("IBD-DIAG: control-loop refill from %u peers (plannerDepth=%u inflight=%u target=%u)\n",
nRefilled, nPlannerDepth, nInFlight, HEADER_SYNC_TARGET_INFLIGHT);
nLastHeaderPlannerControl = nNowSec;
}
if (nNowSec - nLastHeaderWatchdog >= HEADER_SYNC_CONTROL_INTERVAL_SECONDS &&
nNowSec - nLastNewHeaderTime >= HEADER_SYNC_WATCHDOG_SECONDS)
{
const unsigned int nRefilled = RequestRefillAllPeers(
hashBestHeader, HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS,
"headers-watchdog");
if (nRefilled > 0)
printf("IBD-DIAG: headers watchdog refill from %u peers after %llds without new headers (plannerDepth=%u inflight=%u)\n",
nRefilled,
(long long)(nNowSec - nLastNewHeaderTime),
nPlannerDepth,
nInFlight);
nLastHeaderWatchdog = nNowSec;
}
const int64_t nMinInterval = (mapHeaders.size() < HEADER_DOWNLOAD_WINDOW) ? 15 : 60;
if (nNowSec - pto->nLastIbdHeaderRequest >= nMinInterval)
RequestRefill(pto, hashBestHeader, nMinInterval, "heartbeat");
if (nNowSec - nLastBlockPlannerControl >= HEADER_SYNC_CONTROL_INTERVAL_SECONDS &&
hashBestHeader != 0 &&
nPlannerDepth > 0)
{
const unsigned int nRequeued = QueueBlocksParallel(HEADER_DOWNLOAD_WINDOW);
if (nRequeued > 0)
printf("IBD-DIAG: block-planner control queued %u block requests (plannerDepth=%u inflight=%u)\n",
nRequeued, nPlannerDepth, nInFlight);
nLastBlockPlannerControl = nNowSec;
}
if (hashBestHeader == 0 && nHighestInvWalk > nBestHeight &&
hashHighestInvWalk != 0 && mapBlockIndex.count(hashHighestInvWalk))
{
RequestRefill(pto, hashHighestInvWalk, HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS, "inv-walk bridge");
}
}
+58
View File
@@ -0,0 +1,58 @@
// Copyright (c) 2026 The Triangles developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef TRIANGLES_SYNCMANAGER_H
#define TRIANGLES_SYNCMANAGER_H
#include "uint256.h"
#include <cstddef>
#include <cstdint>
#include <vector>
class CBlock;
class CInv;
class CNode;
class CSyncManager
{
public:
struct HeaderNode;
static constexpr unsigned int HEADER_DOWNLOAD_WINDOW = 1024;
static constexpr unsigned int HEADER_SYNC_LOW_WATER = HEADER_DOWNLOAD_WINDOW / 4;
static constexpr unsigned int HEADER_SYNC_TARGET_INFLIGHT = HEADER_DOWNLOAD_WINDOW / 2;
static constexpr int64_t HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS = 5;
static constexpr int64_t HEADER_SYNC_CONTROL_INTERVAL_SECONDS = 5;
static constexpr int64_t HEADER_SYNC_WATCHDOG_SECONDS = 25;
bool HaveHeader(const uint256& hash) const;
uint256 GetBestHeader() const;
std::size_t GetHeaderCount() const;
unsigned int CountInFlight() const;
unsigned int GetPlannerDepth() const;
int GetPlannerHeight() const;
int64_t GetRequestTime(const uint256& hashBlock) const;
bool RequestRefill(CNode* pfrom, uint256 hashTip, int64_t nMinIntervalSeconds, const char* pszReason);
unsigned int RequestRefillAllPeers(uint256 hashTip, int64_t nMinIntervalSeconds, const char* pszReason);
unsigned int QueueBlocksParallel(unsigned int nWindow = HEADER_DOWNLOAD_WINDOW);
bool ProcessHeaders(CNode* pfrom, const std::vector<CBlock>& vHeaders);
void BlockAccepted(const uint256& hashBlock);
void TrackBlockDelivery(CNode* pfrom, const uint256& hashBlock);
void Tick(CNode* pto, int nHighestInvWalk, const uint256& hashHighestInvWalk);
private:
uint256 GetHeaderTrust(unsigned int nBits) const;
bool GetKnownHeaderState(const uint256& hash, int& nHeight, uint256& nChainTrust) const;
bool GetPrevHash(const uint256& hash, uint256& hashPrev) const;
void RecomputeBestHeader();
void PruneHeaders();
bool AddHeaderNode(const CBlock& header, const uint256& hashHeader);
std::vector<uint256> GetDownloadPath(uint256 hashTip) const;
void ContinueHeaders(CNode* pfrom, const uint256& hashTip);
};
extern CSyncManager g_syncManager;
#endif // TRIANGLES_SYNCMANAGER_H
+1 -1
View File
@@ -13,7 +13,7 @@ GetResults(CWalletDB& walletdb, std::map<int64_t, CAccountingEntry>& results)
std::list<CAccountingEntry> aes;
results.clear();
BOOST_CHECK(walletdb.ReorderTransactions(pwalletMain) == DB_LOAD_OK);
BOOST_CHECK(walletdb.ReorderTransactions(pwalletMain.get()) == DB_LOAD_OK);
walletdb.ListAccountCreditDebit("", aes);
for (CAccountingEntry& ae : aes)
{
+1 -1
View File
@@ -46,7 +46,7 @@ struct {
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
CReserveKey reservekey(pwalletMain);
CReserveKey reservekey(pwalletMain.get());
CBlock *pblock;
CTransaction tx;
CScript script;
+5 -5
View File
@@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
{
vector<valtype> solutions;
txnouttype whichType;
TxnOutType whichType;
CScript s;
s << key[0].GetPubKey() << OP_CHECKSIG;
BOOST_CHECK(Solver(s, whichType, solutions));
@@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
}
{
vector<valtype> solutions;
txnouttype whichType;
TxnOutType whichType;
CScript s;
s << OP_DUP << OP_HASH160 << key[0].GetPubKey().GetID() << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(Solver(s, whichType, solutions));
@@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
}
{
vector<valtype> solutions;
txnouttype whichType;
TxnOutType whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, whichType, solutions));
@@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
}
{
vector<valtype> solutions;
txnouttype whichType;
TxnOutType whichType;
CScript s;
s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, whichType, solutions));
@@ -237,7 +237,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
}
{
vector<valtype> solutions;
txnouttype whichType;
TxnOutType whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, whichType, solutions));
+4 -3
View File
@@ -54,7 +54,8 @@
#endif
// Ensure we have the global wallet pointer
extern CWallet* pwalletMain;
#include <memory>
extern std::unique_ptr<CWallet> pwalletMain;
// Static instance
CTorV3Manager* CTorV3Manager::instance = nullptr;
@@ -997,7 +998,7 @@ bool CTorV3Service::ValidateOnionAddress(const std::string& address)
bool CTorV3Service::ExtractKeysFromHex(const std::string& privKeyHex, unsigned char* privKey, unsigned char* pubKey)
{
if (!privKey || !pubKey) {
printf("ERROR: NULL pointers passed to ExtractKeysFromHex\n");
printf("ERROR: nullptr pointers passed to ExtractKeysFromHex\n");
return false;
}
@@ -1063,7 +1064,7 @@ bool CTorV3Service::ExtractKeysFromHex(const std::string& privKeyHex, unsigned c
bool CTorV3Service::DerivePublicKeyFromPrivate(const unsigned char* privateKey, unsigned char* publicKey)
{
if (!privateKey || !publicKey) {
printf("ERROR: NULL pointer passed to DerivePublicKeyFromPrivate\n");
printf("ERROR: nullptr pointer passed to DerivePublicKeyFromPrivate\n");
return false;
}
+50
View File
@@ -16,6 +16,8 @@
#include <thread>
#include <fstream>
#include <cstring>
#include <chrono>
#include <ctime>
#include <vector>
#include <string>
@@ -122,11 +124,59 @@ bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
// Prepare Tor data directory under the wallet's data dir
torDataDir = (::GetDataDir() / "tor_data").string();
fs::create_directories(torDataDir);
// CRITICAL: Tor refuses to use a DataDirectory readable by other users.
// Without 0700, tor_run_main() returns -1 and the embedded Tor never starts.
fs::permissions(torDataDir, fs::perms::owner_all, fs::perm_options::replace);
// triangles fix: auto-repair `state`-as-file corruption (pitfall #19).
// Tor's atomic state-write pattern is: write `state.tmp` → rename to `state`.
// If the daemon is killed or the process crashes mid-write, the rename can
// fail and `state` may be left as a regular file (or a partial file). On
// next start, Tor sees "State file ... is not a file? Failing." and dies
// with code -1 ("Reading config failed"). This was hit on DNS3 on
// 2026-05-24 and on the TRI-LAPTOP GUI wallet on 2026-06-15. The user-facing
// symptom is "Tor failed to start. Triangles requires Tor to operate." and
// the only fix was manually renaming the corrupt file. Detect this state
// here and auto-rename so the daemon is self-healing.
{
fs::path statePath = fs::path(torDataDir) / "state";
std::error_code ec;
if (fs::exists(statePath, ec) && !fs::is_directory(statePath, ec)) {
// state is a file (or symlink to one) — quarantine it
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
char ts[32];
std::strftime(ts, sizeof(ts), "%Y%m%d-%H%M%S", std::gmtime(&t));
fs::path quarantine = fs::path(torDataDir) /
(std::string("state.corrupt-") + ts);
try {
fs::rename(statePath, quarantine, ec);
if (ec) {
// rename can fail on Windows if dest exists; remove then rename
fs::remove(quarantine, ec);
fs::rename(statePath, quarantine, ec);
}
printf("Tor state was a file (corrupt) — quarantined to %s for inspection. Tor will recreate state/ as a directory.\n",
quarantine.filename().string().c_str());
} catch (const std::exception& e) {
printf("WARNING: could not quarantine corrupt Tor state file %s: %s\n",
statePath.string().c_str(), e.what());
// Last resort: try to remove it so Tor can proceed
fs::remove(statePath, ec);
}
}
}
std::string hsDir;
if (hiddenServiceEnabled) {
hsDir = (fs::path(torDataDir) / "hidden_service").string();
fs::create_directories(hsDir);
// CRITICAL: Tor rejects hidden service directories that are not 0700
// ("Permissions on directory ... are too permissive") and aborts config
// validation with code -1. This was the root cause of "Embedded Tor
// exited with code -1" — fs::create_directories honors umask (0022 on
// most Linux systems), leaving the dir at 0755. Force 0700 after creation.
fs::permissions(hsDir, fs::perms::owner_all, fs::perm_options::replace);
}
// Build the argv for tor_run_main
+14 -14
View File
@@ -75,8 +75,8 @@ CTorProcess::CTorProcess()
, hiddenServiceEnabled(true)
, running(false)
#ifdef WIN32
, hProcess(NULL)
, hJob(NULL)
, hProcess(nullptr)
, hJob(nullptr)
, processId(0)
#else
, processId(0)
@@ -97,7 +97,7 @@ std::string CTorProcess::FindTorBinary()
#ifdef WIN32
// Same directory as the wallet executable
char exePath[MAX_PATH];
if (GetModuleFileNameA(NULL, exePath, MAX_PATH)) {
if (GetModuleFileNameA(nullptr, exePath, MAX_PATH)) {
fs::path exeDir = fs::path(exePath).parent_path();
candidates.push_back((exeDir / "tor.exe").string());
candidates.push_back((exeDir / "tor" / "tor.exe").string());
@@ -405,12 +405,12 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
std::string cmdLine = "\"" + torBinaryPath + "\" -f \"" + torrcPath + "\"";
if (!CreateProcessA(
NULL,
nullptr,
(LPSTR)cmdLine.c_str(),
NULL, NULL,
nullptr, nullptr,
FALSE,
CREATE_NO_WINDOW,
NULL, NULL,
nullptr, nullptr,
&si, &pi))
{
DWORD err = ::GetLastError();
@@ -427,7 +427,7 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
// killed via Task Manager. JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE means
// all processes in the job die when the last handle to the job closes
// (i.e. when our process exits for any reason).
hJob = CreateJobObject(NULL, NULL);
hJob = CreateJobObject(nullptr, nullptr);
if (hJob) {
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = {};
jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
@@ -452,7 +452,7 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
execl(torBinaryPath.c_str(), torBinaryPath.c_str(),
"-f", torrcPath.c_str(), (char*)NULL);
"-f", torrcPath.c_str(), (char*)nullptr);
// If exec fails, exit child
_exit(1);
}
@@ -514,16 +514,16 @@ void CTorProcess::Stop()
if (!running) return;
#ifdef WIN32
if (hProcess != NULL) {
if (hProcess != nullptr) {
printf("Stopping Tor process (PID %lu)...\n", processId);
TerminateProcess(hProcess, 0);
WaitForSingleObject(hProcess, 5000);
CloseHandle(hProcess);
hProcess = NULL;
hProcess = nullptr;
}
if (hJob != NULL) {
if (hJob != nullptr) {
CloseHandle(hJob);
hJob = NULL;
hJob = nullptr;
}
#else
if (processId > 0) {
@@ -538,7 +538,7 @@ void CTorProcess::Stop()
}
// Force kill if still running
kill(processId, SIGKILL);
waitpid(processId, NULL, 0);
waitpid(processId, nullptr, 0);
}
#endif
@@ -552,7 +552,7 @@ bool CTorProcess::IsRunning()
if (!running) return false;
#ifdef WIN32
if (hProcess == NULL) return false;
if (hProcess == nullptr) return false;
DWORD exitCode;
if (GetExitCodeProcess(hProcess, &exitCode)) {
return (exitCode == STILL_ACTIVE);
+49 -50
View File
@@ -22,7 +22,6 @@
#include <filesystem>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/asio/ssl.hpp>
#include <fstream>
#include <boost/shared_ptr.hpp>
@@ -44,7 +43,7 @@ static std::string strRPCUserColonPass;
const Object emptyobj;
CNotificationQueue* pNotificationQueue = NULL;
CNotificationQueue* pNotificationQueue = nullptr;
void ThreadRPCServer3(void* parg);
@@ -306,6 +305,10 @@ static const CRPCCommand vRPCCommands[] =
{ "settxfee", &settxfee, false, false },
{ "listsinceblock", &listsinceblock, false, false },
{ "dumpprivkey", &dumpprivkey, false, false },
{ "hdnew", &hdnew, false, false },
{ "hdrestore", &hdrestore, false, false },
{ "hdshow", &hdshow, false, false },
{ "hdinfo", &hdinfo, true, false },
{ "dumpwallet", &dumpwallet, true, false },
{ "importwallet", &importwallet, false, false },
{ "importprivkey", &importprivkey, false, false },
@@ -367,7 +370,7 @@ const CRPCCommand *CRPCTable::operator[](string name) const
{
map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
if (it == mapCommands.end())
return NULL;
return nullptr;
return (*it).second;
}
@@ -401,7 +404,7 @@ string rfc1123Time()
time_t now;
time(&now);
struct tm* now_gmt = gmtime(&now);
string locale(setlocale(LC_TIME, NULL));
string locale(setlocale(LC_TIME, nullptr));
setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
setlocale(LC_TIME, locale.c_str());
@@ -460,13 +463,12 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto,
// Trim trailing \r
if (!str.empty() && str[str.size()-1] == '\r')
str.resize(str.size()-1);
vector<string> vWords;
boost::split(vWords, str, boost::is_any_of(" "));
auto vWords = SplitString(str, ' ');
if (vWords.size() < 2)
return HTTP_INTERNAL_SERVER_ERROR;
proto = 0;
const char *ver = strstr(str.c_str(), "HTTP/1.");
if (ver != NULL)
if (ver != nullptr)
proto = atoi(ver+7);
// Detect request line (GET/POST/...) vs response line (HTTP/1.x ...)
@@ -493,10 +495,10 @@ int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHea
if (nColon != string::npos)
{
string strHeader = str.substr(0, nColon);
boost::trim(strHeader);
boost::to_lower(strHeader);
strHeader = TrimString(strHeader);
strHeader = ToLower(strHeader);
string strValue = str.substr(nColon+1);
boost::trim(strValue);
strValue = TrimString(strValue);
mapHeadersRet[strHeader] = strValue;
if (strHeader == "content-length")
nLen = atoi(strValue.c_str());
@@ -550,7 +552,7 @@ bool HTTPAuthorized(map<string, string>& mapHeaders)
string strAuth = mapHeaders["authorization"];
if (strAuth.size() < 6 || strAuth.substr(0,6) != "Basic ")
return false;
string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
string strUserPass64 = strAuth.substr(6); strUserPass64 = TrimString(strUserPass64);
if (strUserPass64.empty())
return false;
string strUserPass;
@@ -748,7 +750,7 @@ void ThreadRPCServer(void* parg)
PrintException(&e, "ThreadRPCServer()");
} catch (...) {
vnThreadsRunning[THREAD_RPCLISTENER]--;
PrintException(NULL, "ThreadRPCServer()");
PrintException(nullptr, "ThreadRPCServer()");
}
printf("ThreadRPCServer exited\n");
}
@@ -775,12 +777,9 @@ static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketA
acceptor->async_accept(
conn->sslStream.lowest_layer(),
conn->peer,
boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
acceptor,
boost::ref(context),
fUseSSL,
conn,
boost::asio::placeholders::error));
[acceptor, &context, fUseSSL, conn](const boost::system::error_code& error) {
RPCAcceptHandler(acceptor, context, fUseSSL, conn, error);
});
}
/**
@@ -892,17 +891,17 @@ void ThreadRPCServer2(void* parg)
{
context.set_options(ssl::context::no_sslv2);
fs::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
fs::path pathCertFile(GetArg(std::string_view{"-rpcsslcertificatechainfile"}, std::string_view{"server.cert"}));
if (!pathCertFile.is_absolute()) pathCertFile = fs::path(GetDataDir()) / pathCertFile;
if (fs::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string());
else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str());
fs::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
fs::path pathPKFile(GetArg(std::string_view{"-rpcsslprivatekeyfile"}, std::string_view{"server.pem"}));
if (!pathPKFile.is_absolute()) pathPKFile = fs::path(GetDataDir()) / pathPKFile;
if (fs::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem);
else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str());
string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
string strCiphers = GetArg(std::string_view{"-rpcsslciphers"}, std::string_view{"TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"});
SSL_CTX_set_cipher_list(context.native_handle(), strCiphers.c_str());
}
@@ -1341,7 +1340,7 @@ Object CallRPC(const string& strMethod, const Array& params)
asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context);
SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort()))))
if (!d.connect(GetArg(std::string_view{"-rpcconnect"}, std::string_view{"127.0.0.1"}), GetArg(std::string_view{"-rpcport"}, itostr(GetDefaultRPCPort()))))
throw runtime_error("couldn't connect to server");
// HTTP basic authentication
@@ -1415,45 +1414,45 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]);
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "getbalance" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "getblock" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "getblockbynumber" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "getblockbynumber" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "getblockbynumber" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "getblockhash" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
if (strMethod == "move" && n > 3) ConvertTo<int64_t>(params[3]);
if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "sendfrom" && n > 3) ConvertTo<int64_t>(params[3]);
if (strMethod == "listtransactions" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "listtransactions" && n > 2) ConvertTo<int64_t>(params[2]);
if (strMethod == "listaccounts" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "listsinceblock" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "sendmany" && n > 2) ConvertTo<int64_t>(params[2]);
if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "listunspent" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "listunspent" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]);
if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "getrawtransaction" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]);
if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "getblockheader" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "estimatefee" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "estimatefee" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "getaddressbalance" && n > 0) ConvertTo<Object>(params[0]);
if (strMethod == "getaddressutxos" && n > 0) ConvertTo<Object>(params[0]);
if (strMethod == "getaddresstxids" && n > 0) ConvertTo<Object>(params[0]);
@@ -1515,7 +1514,7 @@ int CommandLineRPC(int argc, char *argv[])
}
catch (...)
{
PrintException(NULL, "CommandLineRPC()");
PrintException(nullptr, "CommandLineRPC()");
}
if (strPrint != "")
@@ -1534,18 +1533,18 @@ int main(int argc, char *argv[])
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
_CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0));
#endif
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
setbuf(stdin, nullptr);
setbuf(stdout, nullptr);
setbuf(stderr, nullptr);
try
{
if (argc >= 2 && string(argv[1]) == "-server")
{
printf("server ready\n");
ThreadRPCServer(NULL);
ThreadRPCServer(nullptr);
}
else
{
@@ -1555,7 +1554,7 @@ int main(int argc, char *argv[])
catch (std::exception& e) {
PrintException(&e, "main()");
} catch (...) {
PrintException(NULL, "main()");
PrintException(nullptr, "main()");
}
return 0;
}
+5 -1
View File
@@ -126,7 +126,7 @@ extern const CRPCTable tableRPC;
extern int64_t nWalletUnlockTime;
extern int64_t AmountFromValue(const json_spirit::Value& value);
extern json_spirit::Value ValueFromAmount(int64_t amount);
extern double GetDifficulty(const CBlockIndex* blockindex = NULL);
extern double GetDifficulty(const CBlockIndex* blockindex = nullptr);
extern double GetPoWMHashPS();
extern double GetPoSKernelPS();
@@ -155,6 +155,10 @@ extern json_spirit::Value getwalletinfo(const json_spirit::Array& params, bool f
extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp
extern json_spirit::Value hdnew(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value hdrestore(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value hdshow(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value hdinfo(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getsubsidy(const json_spirit::Array& params, bool fHelp);
+27 -13
View File
@@ -70,7 +70,7 @@ void init_blockindex(leveldb::Options& options, bool fRemoveOld = false) {
CTxDB::CTxDB(const char* pszMode)
{
assert(pszMode);
activeBatch = NULL;
activeBatch = nullptr;
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
if (txdb) {
@@ -97,9 +97,9 @@ CTxDB::CTxDB(const char* pszMode)
printf("Required index version is %d, removing old database\n", DATABASE_VERSION);
delete txdb;
txdb = pdb = NULL;
txdb = pdb = nullptr;
delete activeBatch;
activeBatch = NULL;
activeBatch = nullptr;
init_blockindex(options, true);
pdb = txdb;
@@ -124,13 +124,13 @@ CTxDB::CTxDB(const char* pszMode)
void CTxDB::Close()
{
delete txdb;
txdb = pdb = NULL;
txdb = pdb = nullptr;
delete options.filter_policy;
options.filter_policy = NULL;
options.filter_policy = nullptr;
delete options.block_cache;
options.block_cache = NULL;
options.block_cache = nullptr;
delete activeBatch;
activeBatch = NULL;
activeBatch = nullptr;
}
bool CTxDB::TxnBegin()
@@ -149,7 +149,7 @@ bool CTxDB::TxnCommit()
assert(activeBatch);
leveldb::Status status = pdb->Write(leveldb::WriteOptions(), activeBatch);
delete activeBatch;
activeBatch = NULL;
activeBatch = nullptr;
if (!status.ok()) {
printf("ERROR: LevelDB batch commit failure: %s\n", status.ToString().c_str());
printf("ERROR: This may indicate disk full, corruption, or permissions issue.\n");
@@ -291,7 +291,7 @@ std::unique_ptr<CTxDBIteratorBase> CTxDB::NewIterator() const
static CBlockIndex *InsertBlockIndex(uint256 hash)
{
if (hash == 0)
return NULL;
return nullptr;
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
@@ -372,7 +372,7 @@ bool CTxDB::LoadBlockIndex()
pindexNew->nNonce = diskindex.nNonce;
pindexNew->nChainTrust = diskindex.nChainTrust;
if (pindexGenesisBlock == NULL && blockHash == (!fTestNet ? hashGenesisBlockOfficial : hashGenesisBlockTestNet))
if (pindexGenesisBlock == nullptr && blockHash == (!fTestNet ? hashGenesisBlockOfficial : hashGenesisBlockTestNet))
pindexGenesisBlock = pindexNew;
if (!pindexNew->CheckIndex()) {
@@ -504,7 +504,7 @@ bool CTxDB::LoadBlockIndex()
nPhaseStart = GetTimeMillis();
if (!ReadHashBestChain(hashBestChain))
{
if (pindexGenesisBlock == NULL)
if (pindexGenesisBlock == nullptr)
return true;
return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
}
@@ -514,6 +514,20 @@ bool CTxDB::LoadBlockIndex()
nBestHeight = pindexBest->nHeight;
nBestChainTrust = pindexBest->nChainTrust;
// Heal pnext pointers along the active chain. Persisted hashNext can be
// stale or zeroed by crash-interrupted reorgs, which breaks
// GetKernelStakeModifier()'s forward walk and causes valid new
// proof-of-stake blocks to be rejected with "check kernel failed".
{
int nHealed = 0;
for (CBlockIndex* p = pindexBest; p && p->pprev; p = p->pprev)
{
if (p->pprev->pnext != p) { p->pprev->pnext = p; nHealed++; }
}
if (nHealed > 0)
printf("LoadBlockIndex(): healed %d pnext links on active chain\n", nHealed);
}
printf("STARTUP-PERF: best_chain %" PRId64 "ms\n", GetTimeMillis() - nPhaseStart);
nPhaseStart = GetTimeMillis();
@@ -539,7 +553,7 @@ bool CTxDB::LoadBlockIndex()
// Re-evaluate best chain: scan for competing tips with equal or greater trust.
{
CBlockIndex* pindexBetter = NULL;
CBlockIndex* pindexBetter = nullptr;
for (const auto& item : mapBlockIndex)
{
CBlockIndex* pindex = item.second;
@@ -602,7 +616,7 @@ bool CTxDB::LoadBlockIndex()
if (nCheckDepth > nBestHeight)
nCheckDepth = nBestHeight;
printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CBlockIndex* pindexFork = NULL;
CBlockIndex* pindexFork = nullptr;
map<pair<unsigned int, unsigned int>, CBlockIndex*> mapBlockPos;
for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
{
+2 -1
View File
@@ -42,12 +42,13 @@ public:
bool LoadBlockIndex() override;
std::unique_ptr<CTxDBIteratorBase> NewIterator() const override;
protected:
bool ReadRaw(const std::string& key, std::string& value) const override;
bool WriteRaw(const std::string& key, const std::string& value) override;
bool EraseRaw(const std::string& key) override;
bool ExistsRaw(const std::string& key) const override;
std::unique_ptr<CTxDBIteratorBase> NewIterator() const override;
private:
leveldb::DB* pdb; // Points to the global instance.
+14
View File
@@ -542,6 +542,20 @@ bool CRocksTxDB::LoadBlockIndex()
nBestHeight = pindexBest->nHeight;
nBestChainTrust = pindexBest->nChainTrust;
// Heal pnext pointers along the active chain. Persisted hashNext can be
// stale or zeroed by crash-interrupted reorgs, which breaks
// GetKernelStakeModifier()'s forward walk and causes valid new
// proof-of-stake blocks to be rejected with "check kernel failed".
{
int nHealed = 0;
for (CBlockIndex* p = pindexBest; p && p->pprev; p = p->pprev)
{
if (p->pprev->pnext != p) { p->pprev->pnext = p; nHealed++; }
}
if (nHealed > 0)
printf("LoadBlockIndex(): healed %d pnext links on active chain\n", nHealed);
}
printf("STARTUP-PERF: best_chain %" PRId64 "ms\n", GetTimeMillis() - nPhaseStart);
nPhaseStart = GetTimeMillis();
+11 -1
View File
@@ -38,12 +38,22 @@ public:
bool LoadBlockIndex() override;
// Write a raw serialized key/value pair, bypassing the typed Write<>()
// overloads. Intended for the chaindb migration utility, which carries
// bytes directly across from a CTxDB (LevelDB) iterator. Honors the
// active write batch if one is open.
bool WriteRawRecordForMigration(const std::string& key, const std::string& value)
{
return WriteRaw(key, value);
}
std::unique_ptr<CTxDBIteratorBase> NewIterator() const override;
protected:
bool ReadRaw(const std::string& key, std::string& value) const override;
bool WriteRaw(const std::string& key, const std::string& value) override;
bool EraseRaw(const std::string& key) override;
bool ExistsRaw(const std::string& key) const override;
std::unique_ptr<CTxDBIteratorBase> NewIterator() const override;
private:
rocksdb::DB* pdb; // Points to the global instance.
+64 -21
View File
@@ -40,7 +40,6 @@
#include "strlcpy.h"
#include "version.h"
#include "ui_interface.h"
#include <boost/algorithm/string/join.hpp>
// Work around clang compilation problem in Boost 1.46:
// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup
@@ -127,7 +126,7 @@ public:
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L
// Shutdown OpenSSL library multithreading support (pre-1.1.0 only)
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_locking_callback(nullptr);
for (int i = 0; i < CRYPTO_num_locks(); i++)
delete ppmutexOpenSSL[i];
OPENSSL_free(ppmutexOpenSSL);
@@ -148,7 +147,7 @@ void RandAddSeed()
// Seed with CPU performance counter
int64_t nCounter = GetPerformanceCounter();
RAND_add(&nCounter, sizeof(nCounter), 1.5);
memset(&nCounter, 0, sizeof(nCounter));
OPENSSL_cleanse(&nCounter, sizeof(nCounter));
}
void RandAddSeedPerfmon()
@@ -167,12 +166,12 @@ void RandAddSeedPerfmon()
unsigned char pdata[250000];
memset(pdata, 0, sizeof(pdata));
unsigned long nSize = sizeof(pdata);
long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, pdata, &nSize);
RegCloseKey(HKEY_PERFORMANCE_DATA);
if (ret == ERROR_SUCCESS)
{
RAND_add(pdata, nSize, nSize/100.0);
memset(pdata, 0, nSize);
OPENSSL_cleanse(pdata, nSize);
printf("RandAddSeed() %lu bytes\n", nSize);
}
#endif
@@ -210,7 +209,7 @@ uint256 GetRandHash()
static FILE* fileout = NULL;
static FILE* fileout = nullptr;
inline int OutputDebugStringF(const char* pszFormat, ...)
{
@@ -231,7 +230,7 @@ inline int OutputDebugStringF(const char* pszFormat, ...)
{
std::filesystem::path pathDebug = GetDataDir() / "debug.log";
fileout = fopen(pathDebug.string().c_str(), "a");
if (fileout) setbuf(fileout, NULL); // unbuffered
if (fileout) setbuf(fileout, nullptr); // unbuffered
}
if (fileout)
{
@@ -241,22 +240,22 @@ inline int OutputDebugStringF(const char* pszFormat, ...)
// Since the order of destruction of static/global objects is undefined,
// allocate mutexDebugLog on the heap the first time this routine
// is called to avoid crashes during shutdown.
static std::mutex* mutexDebugLog = NULL;
if (mutexDebugLog == NULL) mutexDebugLog = new std::mutex();
static std::mutex* mutexDebugLog = nullptr;
if (mutexDebugLog == nullptr) mutexDebugLog = new std::mutex();
std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
// reopen the log file, if requested
if (fReopenDebugLog) {
fReopenDebugLog = false;
std::filesystem::path pathDebug = GetDataDir() / "debug.log";
if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
setbuf(fileout, NULL); // unbuffered
if (freopen(pathDebug.string().c_str(),"a",fileout) != nullptr)
setbuf(fileout, nullptr); // unbuffered
}
// Debug print useful for profiling
if (fLogTimestamps && fStartedNewLine)
fprintf(fileout, "%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
if (pszFormat[strlen(pszFormat) - 1] == '\n')
if (pszFormat[0] != '\0' && pszFormat[strlen(pszFormat) - 1] == '\n')
fStartedNewLine = true;
else
fStartedNewLine = false;
@@ -318,7 +317,7 @@ string vstrprintf(const char *format, va_list ap)
delete[] p;
limit *= 2;
p = new char[limit];
if (p == NULL)
if (p == nullptr)
throw std::bad_alloc();
}
string str(p, p+ret);
@@ -464,7 +463,7 @@ static const signed char phexdigit[256] =
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, };
bool IsHex(const string& str)
bool IsHex(std::string_view str)
{
for (unsigned char c : str)
{
@@ -601,6 +600,37 @@ bool SoftSetBoolArg(const std::string& strArg, bool fValue)
return SoftSetArg(strArg, std::string("0"));
}
// C++20 modernization: std::string_view overloads delegating to std::string implementations
std::string GetArg(std::string_view strArg, std::string_view strDefault)
{
return GetArg(std::string(strArg), std::string(strDefault));
}
int64_t GetArg(std::string_view strArg, int64_t nDefault)
{
return GetArg(std::string(strArg), nDefault);
}
bool GetBoolArg(std::string_view strArg, bool fDefault)
{
return GetBoolArg(std::string(strArg), fDefault);
}
bool SoftSetArg(std::string_view strArg, std::string_view strValue)
{
return SoftSetArg(std::string(strArg), std::string(strValue));
}
bool SoftSetBoolArg(std::string_view strArg, bool fValue)
{
return SoftSetBoolArg(std::string(strArg), fValue);
}
bool WildcardMatch(std::string_view str, std::string_view mask)
{
return WildcardMatch(std::string(str), std::string(mask));
}
string EncodeBase64(const unsigned char* pch, size_t len)
{
@@ -970,7 +1000,7 @@ static std::string FormatException(std::exception* pex, const char* pszThread)
{
#ifdef WIN32
char pszModule[MAX_PATH] = "";
GetModuleFileNameA(NULL, pszModule, sizeof(pszModule));
GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule));
#else
const char* pszModule = "Triangles";
#endif
@@ -1031,7 +1061,7 @@ std::filesystem::path GetDefaultDataDir()
#else
fs::path pathRet;
char* pszHome = getenv("HOME");
if (pszHome == NULL || strlen(pszHome) == 0)
if (pszHome == nullptr || strlen(pszHome) == 0)
pathRet = fs::path("/");
else
pathRet = fs::path(pszHome);
@@ -1084,7 +1114,7 @@ const std::filesystem::path &GetDataDir(bool fNetSpecific)
std::filesystem::path GetConfigFile()
{
std::filesystem::path pathConfigFile(GetArg("-conf", "triangles.conf"));
std::filesystem::path pathConfigFile(GetArg(std::string_view{"-conf"}, std::string_view{"triangles.conf"}));
if (!pathConfigFile.is_absolute()) pathConfigFile = GetDataDir(false) / pathConfigFile;
return pathConfigFile;
}
@@ -1115,7 +1145,7 @@ void ReadConfigFile(map<string, string>& mapSettingsRet,
std::filesystem::path GetPidFile()
{
std::filesystem::path pathPidFile(GetArg("-pid", "trianglesd.pid"));
std::filesystem::path pathPidFile(GetArg(std::string_view{"-pid"}, std::string_view{"trianglesd.pid"}));
if (!pathPidFile.is_absolute()) pathPidFile = GetDataDir() / pathPidFile;
return pathPidFile;
}
@@ -1188,7 +1218,7 @@ int64_t GetTime()
{
if (nMockTime) return nMockTime;
return time(NULL);
return time(nullptr);
}
void SetMockTime(int64_t nMockTimeIn)
@@ -1288,7 +1318,7 @@ std::string FormatSubVersion(const std::string& name, int nClientVersion, const
ss << "/";
ss << name << ":" << FormatVersion(nClientVersion);
if (!comments.empty())
ss << "(" << boost::algorithm::join(comments, "; ") << ")";
ss << "(" << JoinStrings(comments, "; ") << ")";
ss << "/";
return ss.str();
}
@@ -1300,7 +1330,7 @@ std::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate)
char pszPath[MAX_PATH] = "";
if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate))
if(SHGetSpecialFolderPathA(nullptr, pszPath, nFolder, fCreate))
{
return fs::path(pszPath);
}
@@ -1349,3 +1379,16 @@ bool NewThread(void(*pfn)(void*), void* parg)
}
return true;
}
template<typename Callable, typename... Args>
bool NewThreadT(Callable&& fn, Args&&... args)
{
try
{
std::thread(std::forward<Callable>(fn), std::forward<Args>(args)...).detach();
} catch(const std::system_error& e) {
printf("Error creating thread: %s\n", e.what());
return false;
}
return true;
}
+98 -18
View File
@@ -17,6 +17,10 @@
#include <map>
#include <vector>
#include <string>
#include <string_view>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <chrono>
#include <thread>
@@ -24,6 +28,18 @@
#include <openssl/sha.h>
#include <openssl/ripemd.h>
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#define TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#define TRI_OPENSSL_SUPPRESS_DEPRECATED_END \
_Pragma("GCC diagnostic pop")
#else
#define TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN
#define TRI_OPENSSL_SUPPRESS_DEPRECATED_END
#endif
#include "netbase.h" // for AddTimeData
@@ -33,8 +49,8 @@
#include <stdint.h>
#include <inttypes.h>
static const int64_t COIN = 1000000;
static const int64_t CENT = 10000;
constexpr int64_t COIN = 1000000;
constexpr int64_t CENT = 10000;
#define BEGIN(a) ((char*)&(a))
#define END(a) ((char*)&((&(a))[1]))
@@ -175,26 +191,29 @@ bool ATTR_WARN_PRINTF(1,2) error(const char *format, ...);
#define printf OutputDebugStringF
void LogException(std::exception* pex, const char* pszThread);
// LogPrintf - variadic macro for logging to stderr (C++20 modernization: restored from removed definition)
#define LogPrintf(...) fprintf(stderr, __VA_ARGS__)
void PrintException(std::exception* pex, const char* pszThread);
void PrintExceptionContinue(std::exception* pex, const char* pszThread);
void ParseString(const std::string& str, char c, std::vector<std::string>& v);
void ParseString(std::string_view str, char c, std::vector<std::string>& v);
std::string FormatMoney(int64_t n, bool fPlus=false);
bool ParseMoney(const std::string& str, int64_t& nRet);
bool ParseMoney(const char* pszIn, int64_t& nRet);
std::vector<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);
bool IsHex(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = NULL);
bool IsHex(std::string_view str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = nullptr);
std::string DecodeBase64(const std::string& str);
std::string EncodeBase64(const unsigned char* pch, size_t len);
std::string EncodeBase64(const std::string& str);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid = NULL);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid = nullptr);
std::string DecodeBase32(const std::string& str);
std::string EncodeBase32(const unsigned char* pch, size_t len);
std::string EncodeBase32(const std::string& str);
void ParseParameters(int argc, const char*const argv[]);
bool WildcardMatch(const char* psz, const char* mask);
bool WildcardMatch(const std::string& str, const std::string& mask);
bool WildcardMatch(std::string_view str, std::string_view mask);
void FileCommit(FILE *fileout);
bool RenameOver(std::filesystem::path src, std::filesystem::path dest);
std::filesystem::path GetDefaultDataDir();
@@ -244,7 +263,7 @@ inline int64_t atoi64(const char* psz)
#ifdef _MSC_VER
return _atoi64(psz);
#else
return strtoll(psz, NULL, 10);
return strtoll(psz, nullptr, 10);
#endif
}
@@ -253,7 +272,7 @@ inline int64_t atoi64(const std::string& str)
#ifdef _MSC_VER
return _atoi64(str.c_str());
#else
return strtoll(str.c_str(), NULL, 10);
return strtoll(str.c_str(), nullptr, 10);
#endif
}
@@ -287,11 +306,57 @@ inline std::string leftTrim(std::string src, char chr)
return src;
}
inline std::string TrimString(std::string str)
{
auto start = str.find_first_not_of(" \t\r\n");
if (start == std::string::npos) return {};
auto end = str.find_last_not_of(" \t\r\n");
return str.substr(start, end - start + 1);
}
inline std::string ToLower(std::string str)
{
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); });
return str;
}
inline void ReplaceAll(std::string& str, const std::string& from, const std::string& to)
{
if (from.empty()) return;
size_t pos = 0;
while ((pos = str.find(from, pos)) != std::string::npos)
{
str.replace(pos, from.length(), to);
pos += to.length();
}
}
inline std::vector<std::string> SplitString(const std::string& str, char delim)
{
std::vector<std::string> tokens;
std::istringstream iss(str);
std::string token;
while (std::getline(iss, token, delim))
tokens.push_back(token);
return tokens;
}
inline std::string JoinStrings(const std::vector<std::string>& parts, const std::string& sep)
{
std::string result;
for (size_t i = 0; i < parts.size(); ++i)
{
if (i > 0) result += sep;
result += parts[i];
}
return result;
}
template<typename T>
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
{
std::string rv;
static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
rv.reserve((itend-itbegin)*3);
for(T it = itbegin; it < itend; ++it)
@@ -329,7 +394,7 @@ inline int64_t GetPerformanceCounter()
QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
#else
timeval t;
gettimeofday(&t, NULL);
gettimeofday(&t, nullptr);
nCounter = (int64_t) t.tv_sec * 1000000 + t.tv_usec;
#endif
return nCounter;
@@ -356,10 +421,10 @@ inline std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime)
return pszTime;
}
static const std::string strTimestampFormat = "%Y-%m-%d %H:%M:%S UTC";
constexpr const char strTimestampFormat[] = "%Y-%m-%d %H:%M:%S UTC";
inline std::string DateTimeStrFormat(int64_t nTime)
{
return DateTimeStrFormat(strTimestampFormat.c_str(), nTime);
return DateTimeStrFormat(strTimestampFormat, nTime);
}
@@ -386,7 +451,7 @@ inline bool IsSwitchChar(char c)
* @param default (e.g. "1")
* @return command-line argument or default value
*/
std::string GetArg(const std::string& strArg, const std::string& strDefault);
std::string GetArg(std::string_view strArg, std::string_view strDefault);
/**
* Return integer argument or default value
@@ -395,7 +460,7 @@ std::string GetArg(const std::string& strArg, const std::string& strDefault);
* @param default (e.g. 1)
* @return command-line argument (0 if invalid number) or default value
*/
int64_t GetArg(const std::string& strArg, int64_t nDefault);
int64_t GetArg(std::string_view strArg, int64_t nDefault);
/**
* Return boolean argument or default value
@@ -404,7 +469,7 @@ int64_t GetArg(const std::string& strArg, int64_t nDefault);
* @param default (true or false)
* @return command-line argument or default value
*/
bool GetBoolArg(const std::string& strArg, bool fDefault=false);
bool GetBoolArg(std::string_view strArg, bool fDefault=false);
/**
* Set an argument if it doesn't already have a value
@@ -413,7 +478,7 @@ bool GetBoolArg(const std::string& strArg, bool fDefault=false);
* @param strValue Value (e.g. "1")
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetArg(const std::string& strArg, const std::string& strValue);
bool SoftSetArg(std::string_view strArg, std::string_view strValue);
/**
* Set a boolean argument if it doesn't already have a value
@@ -422,7 +487,7 @@ bool SoftSetArg(const std::string& strArg, const std::string& strValue);
* @param fValue Value (e.g. false)
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetBoolArg(const std::string& strArg, bool fValue);
bool SoftSetBoolArg(std::string_view strArg, bool fValue);
@@ -453,7 +518,9 @@ public:
int nVersion;
void Init() {
TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN
SHA256_Init(&ctx);
TRI_OPENSSL_SUPPRESS_DEPRECATED_END
}
CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) {
@@ -461,14 +528,18 @@ public:
}
CHashWriter& write(const char *pch, size_t size) {
TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN
SHA256_Update(&ctx, pch, size);
TRI_OPENSSL_SUPPRESS_DEPRECATED_END
return (*this);
}
// invalidates the object
uint256 GetHash() {
uint256 hash1;
TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN
SHA256_Final((unsigned char*)&hash1, &ctx);
TRI_OPENSSL_SUPPRESS_DEPRECATED_END
uint256 hash2;
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
@@ -490,10 +561,12 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end,
static unsigned char pblank[1];
uint256 hash1;
SHA256_CTX ctx;
TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN
SHA256_Init(&ctx);
SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
SHA256_Final((unsigned char*)&hash1, &ctx);
TRI_OPENSSL_SUPPRESS_DEPRECATED_END
uint256 hash2;
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
@@ -507,11 +580,13 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end,
static unsigned char pblank[1];
uint256 hash1;
SHA256_CTX ctx;
TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN
SHA256_Init(&ctx);
SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0]));
SHA256_Final((unsigned char*)&hash1, &ctx);
TRI_OPENSSL_SUPPRESS_DEPRECATED_END
uint256 hash2;
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
@@ -530,7 +605,9 @@ inline uint160 Hash160(const std::vector<unsigned char>& vch)
uint256 hash1;
SHA256(&vch[0], vch.size(), (unsigned char*)&hash1);
uint160 hash2;
TRI_OPENSSL_SUPPRESS_DEPRECATED_BEGIN
RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
TRI_OPENSSL_SUPPRESS_DEPRECATED_END
return hash2;
}
@@ -607,6 +684,9 @@ public:
bool NewThread(void(*pfn)(void*), void* parg);
template<typename Callable, typename... Args>
bool NewThreadT(Callable&& fn, Args&&... args);
#ifdef WIN32
inline void SetThreadPriority(int nPriority)
{
+37 -2
View File
@@ -8,6 +8,12 @@
#include "checkpoints.h"
#include "util.h"
#include "ui_interface.h"
#include "addressindex.h"
#include <variant>
// defined in main.cpp
extern bool fAddressIndex;
#include <filesystem>
@@ -44,7 +50,7 @@ bool DumpSnapshot(const fs::path& destPath,
unsigned int nCollected = 0;
while (pindex && nCollected < nHeaders) {
CDiskBlockIndex diskindex(pindex);
vHeaders.push_back(std::make_pair(*pindex->phashBlock, diskindex));
vHeaders.push_back({*pindex->phashBlock, diskindex});
pindex = pindex->pprev;
nCollected++;
}
@@ -123,7 +129,7 @@ bool DumpSnapshot(const fs::path& destPath,
// documented on CTxDBIteratorBase guarantees a stable view of committed state.
{
CDataStream ssKeyPrefix(SER_DISK, CLIENT_VERSION);
ssKeyPrefix << std::make_pair(std::string("u"), std::make_pair(uint256(0), (unsigned int)0));
ssKeyPrefix << std::pair{std::string("u"), std::pair{uint256(0), (unsigned int)0}};
std::string strPrefixBegin = ssKeyPrefix.str();
auto it = txdbRead.NewIterator();
@@ -190,6 +196,22 @@ bool DumpSnapshot(const fs::path& destPath,
return true;
}
// Extract (type, hash160) from a scriptPubKey for the address index.
// Mirrors GetAddressFromScript() in main.cpp (which is file-static there).
static bool SnapAddressFromScript(const CScript& script, int& nType, uint160& hashBytes)
{
CTxDestination dest;
if (!ExtractDestination(script, dest))
return false;
if (const CKeyID* keyId = std::get_if<CKeyID>(&dest)) {
nType = ADDR_TYPE_P2PKH; hashBytes = *keyId; return true;
}
if (const CScriptID* scriptId = std::get_if<CScriptID>(&dest)) {
nType = ADDR_TYPE_P2SH; hashBytes = *scriptId; return true;
}
return false;
}
// ---------------------------------------------------------------------------
// LoadSnapshot - load a UTXO snapshot into a fresh LevelDB
// ---------------------------------------------------------------------------
@@ -383,6 +405,19 @@ bool LoadSnapshot(const fs::path& snapshotPath,
strError = "WriteUtxo failed at index " + std::to_string(i);
break;
}
// Address index: snapshot UTXOs are all unspent -> credit balance + record UTXO.
if (::fAddressIndex && !entry.scriptPubKey.empty() && entry.nValue != 0) {
int nAType; uint160 aHash;
if (SnapAddressFromScript(entry.scriptPubKey, nAType, aHash)) {
txdb.WriteAddressUtxo(nAType, aHash, txhash, nIndex,
entry.nValue, entry.nHeight, entry.scriptPubKey);
int64_t nABal = 0;
txdb.ReadAddressBalance(nAType, aHash, nABal);
nABal += entry.nValue;
txdb.WriteAddressBalance(nAType, aHash, nABal);
}
}
nBatchSize++;
if (nBatchSize >= 50000) {
+11 -11
View File
@@ -11,7 +11,7 @@
// client versioning
//
static const int CLIENT_VERSION =
constexpr int CLIENT_VERSION =
1000000 * CLIENT_VERSION_MAJOR
+ 10000 * CLIENT_VERSION_MINOR
+ 100 * CLIENT_VERSION_REVISION
@@ -24,35 +24,35 @@ extern const std::string CLIENT_DATE;
//
// database format versioning
//
static const int DATABASE_VERSION = 70509;
constexpr int DATABASE_VERSION = 70509;
//
// network protocol versioning
//
static const int PROTOCOL_VERSION = 70206;
constexpr int PROTOCOL_VERSION = 70206;
// v5 hard fork: require new protocol version (disconnects old nodes)
static const int MIN_PROTO_VERSION = 70205;
constexpr int MIN_PROTO_VERSION = 70205;
// Peers >= this version support the P2P UTXO snapshot protocol
// (getsnap/snap/getsnapchunk/snapchunk and the NODE_SNAPSHOT service flag).
static const int SNAPSHOT_PROTO_VERSION = 70206;
constexpr int SNAPSHOT_PROTO_VERSION = 70206;
static const int INIT_PROTO_VERSION = 209;
constexpr int INIT_PROTO_VERSION = 209;
// nTime field added to CAddress, starting with this version;
// if possible, avoid requesting addresses nodes older than this
static const int CADDR_TIME_VERSION = 70200;
constexpr int CADDR_TIME_VERSION = 70200;
// only request blocks from nodes outside this range of versions
static const int NOBLKS_VERSION_START = 0;
static const int NOBLKS_VERSION_END = 70203;
constexpr int NOBLKS_VERSION_START = 0;
constexpr int NOBLKS_VERSION_END = 70203;
// BIP 0031, pong message, is enabled for all versions AFTER this one
static const int BIP0031_VERSION = 60000;
constexpr int BIP0031_VERSION = 60000;
// "mempool" command, enhanced "getdata" behavior starts with this version:
static const int MEMPOOL_GD_VERSION = 60002;
constexpr int MEMPOOL_GD_VERSION = 60002;
#endif
+226 -155
View File
@@ -7,13 +7,15 @@
#include "wallet.h"
#include "walletdb.h"
#include "crypter.h"
#include "hdwallet.h"
#include "ui_interface.h"
#include "base58.h"
#include "kernel.h"
#include "coincontrol.h"
#include "addressindex.h"
#include "util.h"
#include <cstring>
#include <memory>
#include <boost/algorithm/string/replace.hpp>
#include <algorithm>
#include <random>
#include <deque>
@@ -54,15 +56,19 @@ static CBlockIndex* GetWalletRescanStart(const CWallet& wallet)
}
}
if (wallet.nTimeFirstKey > 1 && pindexBest)
if (wallet.nTimeFirstKey > 1)
{
int64_t nTimeWindowStart = wallet.nTimeFirstKey - 7200;
CBlockIndex* pindexBirthday = pindexBest;
while (pindexBirthday->pprev && pindexBirthday->GetBlockTime() > nTimeWindowStart)
pindexBirthday = pindexBirthday->pprev;
LOCK(cs_main);
if (pindexBest)
{
int64_t nTimeWindowStart = wallet.nTimeFirstKey - 7200;
CBlockIndex* pindexBirthday = pindexBest;
while (pindexBirthday->pprev && pindexBirthday->GetBlockTime() > nTimeWindowStart)
pindexBirthday = pindexBirthday->pprev;
if (!pindexStart || pindexBirthday->nHeight < pindexStart->nHeight)
pindexStart = pindexBirthday;
if (!pindexStart || pindexBirthday->nHeight < pindexStart->nHeight)
pindexStart = pindexBirthday;
}
}
return pindexStart ? pindexStart : pindexGenesisBlock;
@@ -94,11 +100,11 @@ static bool GetIndexedWalletTxHeight(const CTxIndex& txindex, int& nHeight)
if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
return false;
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash());
auto mi = mapBlockIndex.find(block.GetHash());
if (mi == mapBlockIndex.end())
return false;
nHeight = (*mi).second->nHeight;
nHeight = mi->second->nHeight;
return true;
}
@@ -121,15 +127,20 @@ static bool ReadIndexedWalletTransaction(CTxDBBase& txdb, const CDiskTxPos& txPo
CPubKey CWallet::GenerateNewKey()
{
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
bool fCompressed = CanSupportFeature(WalletFeature::ComprPubKey); // default to compressed public keys if we want 0.6.0 wallets
RandAddSeedPerfmon();
CKey key;
key.MakeNewKey(fCompressed);
bool fUsedHD = false;
if (fHDEnabled && !hdMnemonic.empty()) {
if (DeriveHDKey(nHDChainIndex, key)) { fUsedHD = true; fCompressed = true; }
}
if (!fUsedHD)
key.MakeNewKey(fCompressed);
// Compressed public keys were introduced in version 0.6.0
if (fCompressed)
SetMinVersion(FEATURE_COMPRPUBKEY);
SetMinVersion(WalletFeature::ComprPubKey);
CPubKey pubkey = key.GetPubKey();
@@ -141,6 +152,11 @@ CPubKey CWallet::GenerateNewKey()
if (!AddKey(key))
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
if (fUsedHD) {
nHDChainIndex++;
if (fFileBacked)
CWalletDB(strWalletFile).WriteHDChain(nHDChainIndex);
}
return key.GetPubKey();
}
@@ -165,12 +181,8 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector<unsigned char
return true;
{
LOCK(cs_wallet);
if (pwalletdbEncryption)
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
}
return false;
}
bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
@@ -209,6 +221,9 @@ bool CWallet::Lock()
if (fDebug)
printf("Locking wallet.\n");
if (IsCrypted())
hdMnemonic.clear(); // keep only the encrypted copy while locked
{
LOCK(cs_wallet);
CWalletDB wdb(strWalletFile);
@@ -233,8 +248,14 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase)
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
return false;
if (CCryptoKeyStore::Unlock(vMasterKey))
if (CCryptoKeyStore::Unlock(vMasterKey)) {
if (fHDEnabled && hdMnemonic.empty() && !vchCryptedHDMnemonic.empty()) {
CSecret sec;
if (DecryptSecret(vMasterKey, vchCryptedHDMnemonic, hdMnemonicIV, sec))
hdMnemonic.assign(sec.begin(), sec.end());
}
return true;
}
}
SecureMsgWalletUnlocked();
@@ -309,19 +330,19 @@ public:
)
};
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
bool CWallet::SetMinVersion(WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
{
if (nWalletVersion >= nVersion)
if (nWalletVersion >= static_cast<int>(nVersion))
return true;
// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way
if (fExplicit && nVersion > nWalletMaxVersion)
nVersion = FEATURE_LATEST;
if (fExplicit && static_cast<int>(nVersion) > nWalletMaxVersion)
nVersion = WalletFeature::Latest;
nWalletVersion = nVersion;
nWalletVersion = static_cast<int>(nVersion);
if (nVersion > nWalletMaxVersion)
nWalletMaxVersion = nVersion;
if (static_cast<int>(nVersion) > nWalletMaxVersion)
nWalletMaxVersion = static_cast<int>(nVersion);
if (fFileBacked)
{
@@ -396,29 +417,30 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
if (fFileBacked)
{
pwalletdbEncryption = new CWalletDB(strWalletFile);
if (!pwalletdbEncryption->TxnBegin())
std::unique_ptr<CWalletDB> dbEnc(new CWalletDB(strWalletFile));
if (!dbEnc->TxnBegin())
return false;
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
}
dbEnc->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
if (!EncryptKeys(vMasterKey))
{
if (fFileBacked)
pwalletdbEncryption->TxnAbort();
exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet.
}
if (!EncryptKeys(vMasterKey))
{
dbEnc->TxnAbort();
return false;
}
// Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
if (fHDEnabled && !hdMnemonic.empty()) {
CSecret sec(hdMnemonic.begin(), hdMnemonic.end());
uint256 iv = GetRandHash();
std::vector<unsigned char> cipher;
if (!EncryptSecret(vMasterKey, sec, iv, cipher)) { dbEnc->TxnAbort(); return false; }
hdMnemonicIV = iv; vchCryptedHDMnemonic = cipher;
dbEnc->WriteHDCryptedMnemonic(iv, cipher);
}
if (fFileBacked)
{
if (!pwalletdbEncryption->TxnCommit())
exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet.
SetMinVersion(WalletFeature::WalletCrypt, dbEnc.get(), true);
delete pwalletdbEncryption;
pwalletdbEncryption = NULL;
if (!dbEnc->TxnCommit())
return false;
}
Lock();
@@ -456,10 +478,9 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries,
// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
// would make this much faster for applications that do this a lot.
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (auto& [hash, wtx] : mapWallet)
{
CWalletTx* wtx = &((*it).second);
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
txOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
}
acentries.clear();
walletdb.ListAccountCreditDebit(strAccount, acentries);
@@ -480,10 +501,9 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
LOCK(cs_wallet);
for (const CTxIn& txin : tx.vin)
{
map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
if (auto mi = mapWallet.find(txin.prevout.hash); mi != mapWallet.end())
{
CWalletTx& wtx = (*mi).second;
auto& [hash, wtx] = *mi;
if (txin.prevout.n >= wtx.vout.size())
printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str());
else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
@@ -494,7 +514,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
if (!IsInitialBlockDownload())
{
try { NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); }
catch (...) { }
catch (...) { printf("WARNING: NotifyTransactionChanged exception in WalletUpdateSpent\n"); }
}
}
}
@@ -515,7 +535,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
if (!IsInitialBlockDownload())
{
try { NotifyTransactionChanged(this, hash, CT_UPDATED); }
catch (...) { }
catch (...) { printf("WARNING: NotifyTransactionChanged exception in WalletUpdateSpent\n"); }
}
}
}
@@ -662,7 +682,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
if ( !strCmd.empty())
{
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
ReplaceAll(strCmd, "%s", wtxIn.GetHash().GetHex());
std::thread(runCommand, strCmd).detach(); // thread runs free
}
@@ -718,10 +738,9 @@ bool CWallet::IsMine(const CTxIn &txin) const
{
{
LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
if (auto mi = mapWallet.find(txin.prevout.hash); mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
const auto& [hash, prev] = *mi;
if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n]))
return true;
@@ -734,10 +753,9 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const
{
{
LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
if (auto mi = mapWallet.find(txin.prevout.hash); mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
const auto& [hash, prev] = *mi;
if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n]))
return prev.vout[txin.prevout.n].nValue;
@@ -783,27 +801,22 @@ int CWalletTx::GetRequestCount() const
// Generated block
if (hashBlock != 0)
{
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second;
if (auto mi = pwallet->mapRequestCount.find(hashBlock); mi != pwallet->mapRequestCount.end())
nRequests = mi->second;
}
}
else
{
// Did anyone request this transaction?
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
if (mi != pwallet->mapRequestCount.end())
if (auto mi = pwallet->mapRequestCount.find(GetHash()); mi != pwallet->mapRequestCount.end())
{
nRequests = (*mi).second;
nRequests = mi->second;
// How about the block it's in?
if (nRequests == 0 && hashBlock != 0)
{
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second;
if (auto mi2 = pwallet->mapRequestCount.find(hashBlock); mi2 != pwallet->mapRequestCount.end())
nRequests = mi2->second;
else
nRequests = 1; // If it's in someone else's block it must have got out
nRequests = 1;
}
}
}
@@ -891,8 +904,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived,
{
if (pwallet->mapAddressBook.count(r.first))
{
map<CTxDestination, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first);
if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount)
if (auto mi = pwallet->mapAddressBook.find(r.first); mi != pwallet->mapAddressBook.end() && mi->second == strAccount)
nReceived += r.second;
}
else if (strAccount.empty())
@@ -927,11 +939,10 @@ void CWalletTx::AddSupportingTransactions(CTxDBBase& txdb)
setAlreadyDone.insert(hash);
CMerkleTx tx;
map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(hash);
if (mi != pwallet->mapWallet.end())
if (auto mi = pwallet->mapWallet.find(hash); mi != pwallet->mapWallet.end())
{
tx = (*mi).second;
for (const CMerkleTx& txWalletPrev : (*mi).second.vtxPrev)
tx = mi->second;
for (const CMerkleTx& txWalletPrev : mi->second.vtxPrev)
mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;
}
else if (mapWalletPrev.count(hash))
@@ -1026,8 +1037,11 @@ bool CWallet::ScanForWalletTransactionsFromIndex(CBlockIndex* pindexStart, bool
int nFound = 0;
if (pnFound)
*pnFound = 0;
if (!pindexBest)
return false;
{
LOCK(cs_main);
if (!pindexBest)
return false;
}
const int nStartHeight = pindexStart ? pindexStart->nHeight : 0;
@@ -1037,8 +1051,8 @@ bool CWallet::ScanForWalletTransactionsFromIndex(CBlockIndex* pindexStart, bool
set<CScriptID> setScripts;
{
LOCK(cs_KeyStore);
for (ScriptMap::const_iterator it = mapScripts.begin(); it != mapScripts.end(); ++it)
setScripts.insert((*it).first);
for (const auto& [scriptId, script] : mapScripts)
setScripts.insert(scriptId);
}
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
@@ -1114,10 +1128,10 @@ bool CWallet::ScanForWalletTransactionsFromIndex(CBlockIndex* pindexStart, bool
CWalletTx wtx;
{
LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
auto mi = mapWallet.find(hashTx);
if (mi == mapWallet.end())
continue;
wtx = (*mi).second;
wtx = mi->second;
}
// Check UTXO existence to update spent status.
@@ -1144,7 +1158,10 @@ bool CWallet::ScanForWalletTransactionsFromIndex(CBlockIndex* pindexStart, bool
}
}
SetBestChain(CBlockLocator(pindexBest));
{
LOCK(cs_main);
SetBestChain(CBlockLocator(pindexBest));
}
if (pnFound)
*pnFound = nFound;
@@ -1155,7 +1172,7 @@ int CWallet::ScanForWalletTransaction(const uint256& hashTx)
{
CTransaction tx;
tx.ReadFromDisk(COutPoint(hashTx, 0));
if (AddToWalletIfInvolvingMe(tx, NULL, true, true))
if (AddToWalletIfInvolvingMe(tx, nullptr, true, true))
return 1;
return 0;
}
@@ -1318,11 +1335,10 @@ int64_t CWallet::GetBalance() const
int64_t nTotal = 0;
{
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (const auto& [hash, wtx] : mapWallet)
{
const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableCredit();
if (wtx.IsTrusted())
nTotal += wtx.GetAvailableCredit();
}
}
@@ -1334,11 +1350,10 @@ int64_t CWallet::GetUnconfirmedBalance() const
int64_t nTotal = 0;
{
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (const auto& [hash, wtx] : mapWallet)
{
const CWalletTx* pcoin = &(*it).second;
if (!pcoin->IsFinal() || !pcoin->IsTrusted())
nTotal += pcoin->GetAvailableCredit();
if (!wtx.IsFinal() || !wtx.IsTrusted())
nTotal += wtx.GetAvailableCredit();
}
}
return nTotal;
@@ -1349,11 +1364,10 @@ int64_t CWallet::GetImmatureBalance() const
int64_t nTotal = 0;
{
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (const auto& [hash, wtx] : mapWallet)
{
const CWalletTx& pcoin = (*it).second;
if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain())
nTotal += GetCredit(pcoin);
if (wtx.IsCoinBase() && wtx.GetBlocksToMaturity() > 0 && wtx.IsInMainChain())
nTotal += GetCredit(wtx);
}
}
return nTotal;
@@ -1366,9 +1380,9 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
{
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (const auto& [hash, wtx] : mapWallet)
{
const CWalletTx* pcoin = &(*it).second;
const CWalletTx* pcoin = &wtx;
if (!pcoin->IsFinal())
continue;
@@ -1388,7 +1402,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue >= nMinimumInputValue &&
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected(hash, i)))
vCoins.push_back(COutput(pcoin, i, nDepth));
}
@@ -1401,9 +1415,9 @@ void CWallet::AvailableCoinsMinConf(vector<COutput>& vCoins, int nConf) const
{
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (const auto& [hash, wtx] : mapWallet)
{
const CWalletTx* pcoin = &(*it).second;
const CWalletTx* pcoin = &wtx;
if (!pcoin->IsFinal())
continue;
@@ -1461,11 +1475,10 @@ int64_t CWallet::GetStake() const
{
int64_t nTotal = 0;
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (const auto& [hash, wtx] : mapWallet)
{
const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
nTotal += CWallet::GetCredit(*pcoin);
if (wtx.IsCoinStake() && wtx.GetBlocksToMaturity() > 0 && wtx.GetDepthInMainChain() > 0)
nTotal += CWallet::GetCredit(wtx);
}
return nTotal;
}
@@ -1474,11 +1487,10 @@ int64_t CWallet::GetNewMint() const
{
int64_t nTotal = 0;
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (const auto& [hash, wtx] : mapWallet)
{
const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
nTotal += CWallet::GetCredit(*pcoin);
if (wtx.IsCoinBase() && wtx.GetBlocksToMaturity() > 0 && wtx.GetDepthInMainChain() > 0)
nTotal += CWallet::GetCredit(wtx);
}
return nTotal;
}
@@ -1492,9 +1504,9 @@ bool CWallet::GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUncon
TRY_LOCK(cs_wallet, lockWallet);
if (!lockWallet)
return false;
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (const auto& [hash, wtx] : mapWallet)
{
const CWalletTx& pcoin = (*it).second;
const CWalletTx& pcoin = wtx;
if (pcoin.IsCoinStake() && pcoin.GetBlocksToMaturity() > 0 && pcoin.GetDepthInMainChain() > 0)
nStake += CWallet::GetCredit(pcoin);
@@ -1519,7 +1531,7 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime,
// List of values less than target
pair<int64_t, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
coinLowestLarger.first = std::numeric_limits<int64_t>::max();
coinLowestLarger.second.first = NULL;
coinLowestLarger.second.first = nullptr;
vector<pair<int64_t, pair<const CWalletTx*,unsigned int> > > vValue;
int64_t nTotalLower = 0;
@@ -1571,7 +1583,7 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime,
if (nTotalLower < nTargetValue)
{
if (coinLowestLarger.second.first == NULL)
if (coinLowestLarger.second.first == nullptr)
return false;
setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
@@ -1801,7 +1813,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
// Check that enough fee is included
int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000);
int64_t nMinFee = wtxNew.GetMinFee(1, GMF_SEND, nBytes);
int64_t nMinFee = wtxNew.GetMinFee(1, GetMinFeeMode::Send, nBytes);
if (nFeeRet < max(nPayFee, nMinFee))
{
@@ -1930,7 +1942,11 @@ bool CWallet::GetStakeWeight(const CKeyStore& keystore, uint64_t& nMinWeight, ui
bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64_t nSearchInterval, int64_t nFees, CTransaction& txNew, CKey& key)
{
CBlockIndex* pindexPrev = pindexBest;
CBlockIndex* pindexPrev;
{
LOCK(cs_main);
pindexPrev = pindexBest;
}
CBigNum bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
@@ -1997,7 +2013,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
if (fDebug && GetBoolArg("-printcoinstake"))
printf("CreateCoinStake : kernel found\n");
vector<valtype> vSolutions;
txnouttype whichType;
TxnOutType whichType;
CScript scriptPubKeyOut;
scriptPubKeyKernel = pcoin.first->vout[pcoin.second].scriptPubKey;
if (!Solver(scriptPubKeyKernel, whichType, vSolutions))
@@ -2007,31 +2023,30 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
break;
}
if (fDebug && GetBoolArg("-printcoinstake"))
printf("CreateCoinStake : parsed kernel type=%d\n", whichType);
if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH)
printf("CreateCoinStake : parsed kernel type=%d\n", static_cast<int>(whichType));
if (whichType != TxnOutType::PubKey && whichType != TxnOutType::PubKeyHash)
{
if (fDebug && GetBoolArg("-printcoinstake"))
printf("CreateCoinStake : no support for kernel type=%d\n", whichType);
break; // only support pay to public key and pay to address
printf("CreateCoinStake : no support for kernel type=%d\n", static_cast<int>(whichType));
break;
}
if (whichType == TX_PUBKEYHASH) // pay to address type
if (whichType == TxnOutType::PubKeyHash)
{
// convert to pay to public key type
if (!keystore.GetKey(uint160(vSolutions[0]), key))
{
if (fDebug && GetBoolArg("-printcoinstake"))
printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
break; // unable to find corresponding public key
printf("CreateCoinStake : failed to get key for kernel type=%d\n", static_cast<int>(whichType));
break;
}
scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG;
}
if (whichType == TX_PUBKEY)
if (whichType == TxnOutType::PubKey)
{
valtype& vchPubKey = vSolutions[0];
if (!keystore.GetKey(Hash160(vchPubKey), key))
{
if (fDebug && GetBoolArg("-printcoinstake"))
printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
printf("CreateCoinStake : failed to get key for kernel type=%d\n", static_cast<int>(whichType));
break; // unable to find corresponding public key
}
@@ -2160,7 +2175,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
// This is only to keep the database open to defeat the auto-flush for the
// duration of this scope. This is the only place where this optimization
// maybe makes sense; please don't do it anywhere else.
CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL;
CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : nullptr;
// Take key pair from key pool so it won't be used again
reservekey.KeepKey();
@@ -2178,7 +2193,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
coin.MarkSpent(txin.prevout.n);
coin.WriteToDisk();
try { NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); }
catch (...) { }
catch (...) { printf("WARNING: NotifyTransactionChanged exception in CommitTransaction\n"); }
}
if (fFileBacked)
@@ -2295,7 +2310,7 @@ bool CWallet::SetAddressBookName(const CTxDestination& address, const string& st
ChangeType nMode;
{
LOCK(cs_wallet); // mapAddressBook
std::map<CTxDestination, std::string>::iterator mi = mapAddressBook.find(address);
auto mi = mapAddressBook.find(address);
nMode = (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED;
fOwned = ::IsMine(*this, address);
@@ -2308,7 +2323,7 @@ bool CWallet::SetAddressBookName(const CTxDestination& address, const string& st
SecureMsgWalletKeyChanged(caddress.ToString(), strName, nMode);
}
try { NotifyAddressBookChanged(this, address, strName, fOwned, nMode); }
catch (...) { }
catch (...) { printf("WARNING: NotifyAddressBookChanged exception in SetAddressBookName\n"); }
if (!fFileBacked)
return false;
@@ -2331,7 +2346,7 @@ bool CWallet::DelAddressBookName(const CTxDestination& address)
SecureMsgWalletKeyChanged(caddress.ToString(), sName, CT_DELETED);
}
try { NotifyAddressBookChanged(this, address, "", fOwned, CT_DELETED); }
catch (...) { }
catch (...) { printf("WARNING: NotifyAddressBookChanged exception in DelAddressBookName\n"); }
if (!fFileBacked)
return false;
@@ -2362,10 +2377,9 @@ bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx)
{
{
LOCK(cs_wallet);
map<uint256, CWalletTx>::iterator mi = mapWallet.find(hashTx);
if (mi != mapWallet.end())
if (auto mi = mapWallet.find(hashTx); mi != mapWallet.end())
{
wtx = (*mi).second;
wtx = mi->second;
return true;
}
}
@@ -2686,8 +2700,8 @@ void CWallet::FixSpentCoins(int& nMismatchFound, int64_t& nBalanceInQuestion, bo
LOCK(cs_wallet);
vector<CWalletTx*> vCoins;
vCoins.reserve(mapWallet.size());
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
vCoins.push_back(&(*it).second);
for (auto& [hash, wtx] : mapWallet)
vCoins.push_back(&wtx);
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
for (CWalletTx* pcoin : vCoins)
@@ -2737,10 +2751,9 @@ void CWallet::DisableTransaction(const CTransaction &tx)
LOCK(cs_wallet);
for (const CTxIn& txin : tx.vin)
{
map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
if (auto mi = mapWallet.find(txin.prevout.hash); mi != mapWallet.end())
{
CWalletTx& prev = (*mi).second;
auto& [hash, prev] = *mi;
if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n]))
{
prev.MarkUnspent(txin.prevout.n);
@@ -2809,11 +2822,10 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx)
{
LOCK(cs_wallet);
// Only notify UI if this transaction is in this wallet
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
if (mi != mapWallet.end() && !IsInitialBlockDownload())
if (auto mi = mapWallet.find(hashTx); mi != mapWallet.end() && !IsInitialBlockDownload())
{
try { NotifyTransactionChanged(this, hashTx, CT_UPDATED); }
catch (...) { }
catch (...) { printf("WARNING: NotifyTransactionChanged exception in UpdatedTransaction\n"); }
}
}
}
@@ -2822,9 +2834,9 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
mapKeyBirth.clear();
// get birth times for keys with metadata
for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++)
if (it->second.nCreateTime)
mapKeyBirth[it->first] = it->second.nCreateTime;
for (const auto& [keyId, meta] : mapKeyMetadata)
if (meta.nCreateTime)
mapKeyBirth[keyId] = meta.nCreateTime;
// map in which we'll infer heights of other keys
CBlockIndex *pindexMax = FindBlockByHeight(std::max(0, nBestHeight - 144)); // the tip can be reorganised; use a 144-block safety margin
@@ -2843,11 +2855,8 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
// find first block that affects those keys, if there are any left
std::vector<CKeyID> vAffected;
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) {
// iterate over all wallet transactions...
const CWalletTx &wtx = (*it).second;
std::map<uint256, CBlockIndex*>::const_iterator blit = mapBlockIndex.find(wtx.hashBlock);
if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) {
for (const auto& [hash, wtx] : mapWallet) {
if (auto blit = mapBlockIndex.find(wtx.hashBlock); blit != mapBlockIndex.end() && blit->second->IsInMainChain()) {
// ... which are already in a block
int nHeight = blit->second->nHeight;
for (const CTxOut &txout : wtx.vout) {
@@ -2855,8 +2864,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
::ExtractAffectedKeys(*this, txout.scriptPubKey, vAffected);
for (const CKeyID &keyid : vAffected) {
// ... and all their affected keys
std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid);
if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)
if (auto rit = mapKeyFirstBlock.find(keyid); rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)
rit->second = blit->second;
}
vAffected.clear();
@@ -2865,8 +2873,71 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
}
// Extract block timestamps for those keys
for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off
for (const auto& [keyId, pindex] : mapKeyFirstBlock)
mapKeyBirth[keyId] = pindex->nTime - 7200; // block times can be 2h off
}
// ---- HD wallet (BIP39/BIP32) implementation ----
bool CWallet::DeriveHDKey(int64_t index, CKey& keyOut) const
{
if (hdMnemonic.empty())
return false;
unsigned char priv[32];
if (!hd::DeriveTriangles(hdMnemonic, "", 0, 0, (uint32_t)index, priv))
return false;
CSecret secret(priv, priv + 32);
memset(priv, 0, sizeof(priv));
keyOut.SetSecret(secret, true); // HD keys are compressed
return true;
}
bool CWallet::GetHDMnemonic(std::string& mnemonicOut) const
{
if (!fHDEnabled || hdMnemonic.empty())
return false;
mnemonicOut = hdMnemonic;
return true;
}
bool CWallet::SetHDSeed(const std::string& mnemonicIn, const std::string& passphrase, bool fGenerate, std::string& mnemonicOut, std::string& strError)
{
LOCK(cs_wallet);
if (IsLocked()) { strError = "Wallet is locked; unlock it before setting an HD seed."; return false; }
std::string m = mnemonicIn;
if (m.empty()) {
if (!fGenerate) { strError = "No mnemonic supplied."; return false; }
m = hd::GenerateMnemonic(256);
if (m.empty()) { strError = "Failed to generate mnemonic."; return false; }
}
if (!hd::CheckMnemonic(m)) { strError = "Invalid mnemonic (unknown word or bad checksum)."; return false; }
unsigned char priv[32];
if (!hd::DeriveTriangles(m, passphrase, 0, 0, 0, priv)) { strError = "Key derivation failed."; return false; }
memset(priv, 0, sizeof(priv));
hdMnemonic = m;
fHDEnabled = true;
nHDChainIndex = 0;
if (fFileBacked) {
CWalletDB wdb(strWalletFile);
if (IsCrypted()) {
CSecret sec(m.begin(), m.end());
uint256 iv = GetRandHash();
std::vector<unsigned char> cipher;
if (!EncryptSecret(vMasterKey, sec, iv, cipher)) { strError = "Failed to encrypt seed."; return false; }
hdMnemonicIV = iv; vchCryptedHDMnemonic = cipher;
wdb.WriteHDCryptedMnemonic(iv, cipher);
} else {
wdb.WriteHDMnemonic(m);
}
wdb.WriteHDChain(nHDChainIndex);
}
// Replace any pre-existing (random) keypool with HD-derived keys so that
// getnewaddress immediately hands out deterministic m/44'/2222'/0'/0/i keys.
NewKeyPool();
mnemonicOut = m;
return true;
}
+44 -37
View File
@@ -29,17 +29,15 @@ class COutput;
class CCoinControl;
//typedef std::map<CKeyID, CStealthKeyMetadata> StealthKeyMetaMap;
typedef std::map<std::string, std::string> mapValue_t;
using mapValue_t = std::map<std::string, std::string>;
/** (client) version numbers for particular wallet features */
enum WalletFeature
enum class WalletFeature : int
{
FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output)
FEATURE_WALLETCRYPT = 40000, // wallet encryption
FEATURE_COMPRPUBKEY = 60000, // compressed public keys
FEATURE_LATEST = 60000
Base = 10500,
WalletCrypt = 40000,
ComprPubKey = 60000,
Latest = 60000
};
/** A key pool entry */
@@ -76,7 +74,7 @@ class CWallet : public CCryptoKeyStore
{
private:
bool SelectCoinsSimple(int64_t nTargetValue, unsigned int nSpendTime, int nMinConf, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const;
bool SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl=NULL) const;
bool SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl=nullptr) const;
CWalletDB *pwalletdbEncryption;
@@ -102,26 +100,21 @@ public:
CWallet()
{
nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE;
nWalletVersion = static_cast<int>(WalletFeature::Base);
nWalletMaxVersion = static_cast<int>(WalletFeature::Base);
fFileBacked = false;
nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL;
pwalletdbEncryption = nullptr;
fHDEnabled = false;
nHDChainIndex = 0;
nOrderPosNext = 0;
nCachedStakeWeight = 0;
nCachedStakeWeightTime = 0;
}
CWallet(std::string strWalletFileIn)
CWallet(std::string strWalletFileIn) : CWallet()
{
nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE;
strWalletFile = strWalletFileIn;
fFileBacked = true;
nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL;
nOrderPosNext = 0;
nCachedStakeWeight = 0;
nCachedStakeWeightTime = 0;
}
std::map<uint256, CWalletTx> mapWallet;
@@ -133,15 +126,30 @@ public:
CPubKey vchDefaultKey;
int64_t nTimeFirstKey;
// ---- HD (BIP39/BIP32) wallet state ----
bool fHDEnabled; // an HD seed has been set
int64_t nHDChainIndex; // next external index (m/44'/2222'/0'/0/n)
std::string hdMnemonic; // in-memory phrase (present when unlocked/unencrypted)
std::vector<unsigned char> vchCryptedHDMnemonic; // encrypted phrase (loaded, decrypted on unlock)
uint256 hdMnemonicIV; // IV for the encrypted phrase
// check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; }
bool CanSupportFeature(WalletFeature wf) { return nWalletMaxVersion >= static_cast<int>(wf); }
void AvailableCoinsMinConf(std::vector<COutput>& vCoins, int nConf) const;
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL) const;
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=nullptr) const;
bool SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const;
// keystore implementation
// Generate a new key
CPubKey GenerateNewKey();
// ---- HD wallet (BIP39/BIP32) ----
bool IsHDEnabled() const { return fHDEnabled; }
bool SetHDSeed(const std::string& mnemonicIn, const std::string& passphrase, bool fGenerate, std::string& mnemonicOut, std::string& strError);
bool GetHDMnemonic(std::string& mnemonicOut) const;
bool DeriveHDKey(int64_t index, CKey& keyOut) const;
bool LoadHDMnemonic(const std::string& m) { hdMnemonic = m; fHDEnabled = true; return true; }
bool LoadCryptedHDMnemonic(const uint256& iv, const std::vector<unsigned char>& cipher) { hdMnemonicIV = iv; vchCryptedHDMnemonic = cipher; fHDEnabled = true; return true; }
// Adds a key to the store, and saves it to disk.
bool AddKey(const CKey& key);
// Adds a key to the store, without saving it to disk (used by LoadWallet)
@@ -169,7 +177,7 @@ public:
/** Increment the next transaction order id
@return next transaction order id
*/
int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL);
int64_t IncOrderPosNext(CWalletDB *pwalletdb = nullptr);
typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;
typedef std::multimap<int64_t, TxPair > TxItems;
@@ -186,7 +194,7 @@ public:
bool EraseFromWallet(uint256 hash);
void WalletUpdateSpent(const CTransaction& prevout, bool fBlock = false);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
bool ScanForWalletTransactionsFromIndex(CBlockIndex* pindexStart, bool fUpdate, int* pnFound = NULL);
bool ScanForWalletTransactionsFromIndex(CBlockIndex* pindexStart, bool fUpdate, int* pnFound = nullptr);
int ScanForWalletTransaction(const uint256& hashTx);
void ReacceptWalletTransactions();
void ResendWalletTransactions(bool fForce = false);
@@ -197,8 +205,8 @@ public:
int64_t GetNewMint() const;
// Get all balances in a single lock acquisition + single pass (avoids 4x lock + 4x iteration)
bool GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUnconfirmed, int64_t& nImmature) const;
bool CreateTransaction(const std::vector<std::pair<CScript, int64_t> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL);
bool CreateTransaction(CScript scriptPubKey, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL);
bool CreateTransaction(const std::vector<std::pair<CScript, int64_t> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=nullptr);
bool CreateTransaction(CScript scriptPubKey, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=nullptr);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
bool GetStakeWeight(const CKeyStore& keystore, uint64_t& nMinWeight, uint64_t& nMaxWeight, uint64_t& nWeight);
@@ -225,9 +233,9 @@ public:
std::set< std::set<CTxDestination> > GetAddressGroupings();
std::map<CTxDestination, int64_t> GetAddressBalances();
bool IsMine(const CTxIn& txin) const;
[[nodiscard]] bool IsMine(const CTxIn& txin) const;
int64_t GetDebit(const CTxIn& txin) const;
bool IsMine(const CTxOut& txout) const
[[nodiscard]] bool IsMine(const CTxOut& txout) const
{
return ::IsMine(*this, txout.scriptPubKey);
}
@@ -244,7 +252,7 @@ public:
throw std::runtime_error("CWallet::GetChange() : value out of range");
return (IsChange(txout) ? txout.nValue : 0);
}
bool IsMine(const CTransaction& tx) const
[[nodiscard]] bool IsMine(const CTransaction& tx) const
{
for (const CTxOut& txout : tx.vout)
if (IsMine(txout) && txout.nValue >= nMinimumInputValue)
@@ -320,7 +328,7 @@ public:
bool SetDefaultKey(const CPubKey &vchPubKey);
// signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower
bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false);
bool SetMinVersion(WalletFeature, CWalletDB* pwalletdbIn = nullptr, bool fExplicit = false);
// change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format)
bool SetMaxVersion(int nVersion);
@@ -368,8 +376,6 @@ public:
};
typedef std::map<std::string, std::string> mapValue_t;
static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
{
@@ -422,7 +428,7 @@ public:
CWalletTx()
{
Init(NULL);
Init(nullptr);
}
CWalletTx(const CWallet* pwalletIn)
@@ -467,7 +473,7 @@ public:
(
CWalletTx* pthis = const_cast<CWalletTx*>(this);
if (fRead)
pthis->Init(NULL);
pthis->Init(nullptr);
char fSpent = false;
if (!fRead)
@@ -592,6 +598,7 @@ public:
{
if (vin.empty())
return 0;
LOCK(pwallet->cs_wallet);
if (fDebitCached)
return nDebitCached;
nDebitCached = pwallet->GetDebit(*this);
@@ -601,11 +608,10 @@ public:
int64_t GetCredit(bool fUseCache=true) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
// GetBalance can assume transactions in mapWallet won't change
LOCK(pwallet->cs_wallet);
if (fUseCache && fCreditCached)
return nCreditCached;
nCreditCached = pwallet->GetCredit(*this);
@@ -615,10 +621,10 @@ public:
int64_t GetAvailableCredit(bool fUseCache=true) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
LOCK(pwallet->cs_wallet);
if (fUseCache && fAvailableCreditCached)
return nAvailableCreditCached;
@@ -642,6 +648,7 @@ public:
int64_t GetChange() const
{
LOCK(pwallet->cs_wallet);
if (fChangeCached)
return nChangeCached;
nChangeCached = pwallet->GetChange(*this);
+20 -1
View File
@@ -429,6 +429,24 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
}
else if (strType == "hdmnemonic")
{
std::string m;
ssValue >> m;
pwallet->LoadHDMnemonic(m);
}
else if (strType == "hdcmnemonic")
{
std::pair<uint256, std::vector<unsigned char> > cm;
ssValue >> cm;
pwallet->LoadCryptedHDMnemonic(cm.first, cm.second);
}
else if (strType == "hdchain")
{
int64_t n;
ssValue >> n;
pwallet->nHDChainIndex = n;
}
else if (strType == "version")
{
ssValue >> wss.nFileVersion;
@@ -461,7 +479,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
static bool IsKeyType(string strType)
{
return (strType== "key" || strType == "wkey" ||
strType == "mkey" || strType == "ckey");
strType == "mkey" || strType == "ckey" ||
strType == "hdmnemonic" || strType == "hdcmnemonic");
}
DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
+15
View File
@@ -169,6 +169,21 @@ public:
return Write(std::string("defaultkey"), vchPubKey.Raw());
}
bool WriteHDMnemonic(const std::string& mnemonic) {
nWalletDBUpdated++;
Erase(std::string("hdcmnemonic"));
return Write(std::string("hdmnemonic"), mnemonic);
}
bool WriteHDCryptedMnemonic(const uint256& iv, const std::vector<unsigned char>& cipher) {
nWalletDBUpdated++;
Erase(std::string("hdmnemonic"));
return Write(std::string("hdcmnemonic"), std::make_pair(iv, cipher));
}
bool WriteHDChain(int64_t nIndex) {
nWalletDBUpdated++;
return Write(std::string("hdchain"), nIndex);
}
bool ReadPool(int64_t nPool, CKeyPool& keypool)
{
return Read(std::make_pair(std::string("pool"), nPool), keypool);
+7 -7
View File
@@ -13,10 +13,10 @@
#include <zmq.h>
#include <string.h>
CZMQPublishNotifier* pzmqNotifier = NULL;
CZMQPublishNotifier* pzmqNotifier = nullptr;
CZMQPublishNotifier::CZMQPublishNotifier()
: pcontext(NULL), psocket(NULL), fInitialized(false)
: pcontext(nullptr), psocket(nullptr), fInitialized(false)
{
}
@@ -40,7 +40,7 @@ bool CZMQPublishNotifier::Initialize(const std::string& addr)
{
printf("ZMQ: Failed to create socket\n");
zmq_ctx_destroy(pcontext);
pcontext = NULL;
pcontext = nullptr;
return false;
}
@@ -50,8 +50,8 @@ bool CZMQPublishNotifier::Initialize(const std::string& addr)
printf("ZMQ: Failed to bind to %s: %s\n", address.c_str(), zmq_strerror(errno));
zmq_close(psocket);
zmq_ctx_destroy(pcontext);
psocket = NULL;
pcontext = NULL;
psocket = nullptr;
pcontext = nullptr;
return false;
}
@@ -65,12 +65,12 @@ void CZMQPublishNotifier::Shutdown()
if (psocket)
{
zmq_close(psocket);
psocket = NULL;
psocket = nullptr;
}
if (pcontext)
{
zmq_ctx_destroy(pcontext);
pcontext = NULL;
pcontext = nullptr;
}
fInitialized = false;
}