Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d0a76f8ae2 | |||
| cc57c906b4 | |||
| e80d672833 | |||
| fcdc9a58b0 | |||
| 514867c5d9 | |||
| c464e6c59d | |||
| 11ed086d1e | |||
| 5511cfae6b | |||
| 1dda8b3006 | |||
| 68c4e38411 | |||
| ed996c8e9d | |||
| cd9e023865 | |||
| f273d651ed | |||
| 2ac88b4e0a | |||
| c1340441cc | |||
| c99d859781 | |||
| 713f69e137 | |||
| e3f397c7e5 | |||
| f80fb98f68 | |||
| 610b61b1b1 | |||
| e8edcd4aa6 | |||
| 3928f86657 | |||
| 67d838433d | |||
| e3aeff002c | |||
| 09ccd4339c | |||
| 03aa38f1b4 | |||
| 9389a883f1 | |||
| 31fa26f03a | |||
| ce96d278cd | |||
| 7166b76bad | |||
| 540db0e210 | |||
| 59b75476ca | |||
| 0029b34698 | |||
| 8e03e89764 | |||
| 3b1850af9d | |||
| 223029785c | |||
| ce8be45ea5 | |||
| e6d8c6dbfe | |||
| 74ec53040c | |||
| e251a85d7a | |||
| a16533f11b | |||
| b979f7ae7d | |||
| b1e9878849 | |||
| b47fa91d6c | |||
| e9e4a0ca82 | |||
| f5e2ce5ca9 | |||
| cdbbbb5316 | |||
| c5967e9995 | |||
| 5f61ed8fcb | |||
| c3e4a456d8 | |||
| 080942b49d | |||
| 1220168faf | |||
| 4b8d5ab8b1 | |||
| 9d80ddb6ac | |||
| 150828b806 | |||
| 372b252294 | |||
| 6c56e41e82 | |||
| 79b0c4a176 | |||
| 3db537d759 | |||
| 63be053b1d | |||
| d0fb2dc105 | |||
| bea3c4447c | |||
| 89a480a85a | |||
| 47e358dc18 | |||
| 47c9293849 | |||
| 0f5582f100 | |||
| 3a78a6baf9 | |||
| 0f2cf711db | |||
| 55c202516d | |||
| 4e1a0576e1 | |||
| b4308e42ad | |||
| c4656ac244 | |||
| 2da1c039a8 | |||
| 2b701f6640 | |||
| de4498b8eb | |||
| 6d70b41844 | |||
| 815cc02aa9 | |||
| 4fa30abb4b | |||
| 68a86c38b5 | |||
| 3099371864 | |||
| 42653434e0 | |||
| 25475d1057 | |||
| ce27e8e5cf | |||
| b17a004b83 | |||
| ccfada5ca9 | |||
| 59ee532bf6 | |||
| 03073bd597 | |||
| 674bdc7192 | |||
| 76579e3059 | |||
| 426e23d8be | |||
| 269498453e | |||
| 32330b420e | |||
| 2ba0ecf428 |
@@ -0,0 +1,49 @@
|
||||
# Triangles code style.
|
||||
# Conservative: do not reflow long lines, do not reorganize includes.
|
||||
# This config is enforced *only on changed lines* via `git clang-format` in CI,
|
||||
# so it shapes new/edited code without touching legacy files until they're touched.
|
||||
|
||||
BasedOnStyle: LLVM
|
||||
Language: Cpp
|
||||
Standard: c++17
|
||||
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
ContinuationIndentWidth: 4
|
||||
AccessModifierOffset: -4
|
||||
|
||||
ColumnLimit: 0 # Don't reflow long lines — too disruptive for legacy code.
|
||||
ReflowComments: false
|
||||
|
||||
BreakBeforeBraces: Attach
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
|
||||
PointerAlignment: Left
|
||||
DerivePointerAlignment: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
|
||||
NamespaceIndentation: None
|
||||
FixNamespaceComments: true
|
||||
|
||||
# Includes: don't shuffle — header order in this codebase is load-bearing
|
||||
# (e.g. main.cpp's mix of project + system headers carries platform meaning).
|
||||
SortIncludes: false
|
||||
IncludeBlocks: Preserve
|
||||
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MaxEmptyLinesToKeep: 2
|
||||
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignTrailingComments: true
|
||||
|
||||
# Don't auto-add braces to single-statement bodies — too invasive.
|
||||
InsertBraces: false
|
||||
@@ -0,0 +1,46 @@
|
||||
# Triangles clang-tidy config.
|
||||
#
|
||||
# Goal: catch real bugs in new/edited code without drowning in noise from
|
||||
# legacy patterns. Enforced *diff-only* in CI (changed lines on PRs).
|
||||
#
|
||||
# Conservative starter set. Graduate checks to WarningsAsErrors only after
|
||||
# the codebase is clean for that check.
|
||||
|
||||
Checks: >
|
||||
-*,
|
||||
bugprone-*,
|
||||
performance-*,
|
||||
readability-misleading-indentation,
|
||||
readability-redundant-control-flow,
|
||||
readability-redundant-smartptr-get,
|
||||
readability-redundant-string-cstr,
|
||||
readability-redundant-string-init,
|
||||
readability-string-compare,
|
||||
modernize-use-nullptr,
|
||||
modernize-use-override,
|
||||
modernize-deprecated-headers,
|
||||
cppcoreguidelines-init-variables,
|
||||
cppcoreguidelines-pro-type-member-init,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-signed-char-misuse,
|
||||
-bugprone-reserved-identifier,
|
||||
-bugprone-unchecked-optional-access,
|
||||
-performance-no-int-to-ptr,
|
||||
-performance-avoid-endl
|
||||
|
||||
# Warn-only initially. Once a check is clean repo-wide we can promote it here.
|
||||
WarningsAsErrors: ''
|
||||
|
||||
# Run on project sources; skip vendored/generated code.
|
||||
HeaderFilterRegex: '^.*src/(?!json/nlohmann_json|leveldb|lz4|tor/tor-src).*\.h$'
|
||||
|
||||
FormatStyle: file
|
||||
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.IgnoreMainLikeFunctions
|
||||
value: '1'
|
||||
- key: cppcoreguidelines-init-variables.IncludeStyle
|
||||
value: 'google'
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"ls /mingw64/lib/libboost_system* 2>/dev/null\")",
|
||||
"Bash(git tag:*)"
|
||||
],
|
||||
"additionalDirectories": [
|
||||
"C:\\msys64\\mingw64\\bin"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# Revisions listed here are skipped by `git blame` when --ignore-revs-file
|
||||
# is configured. GitHub honors this file automatically.
|
||||
#
|
||||
# Add the SHA of any large mechanical reformat / rename / mass-style commit
|
||||
# below, with a one-line comment.
|
||||
#
|
||||
# Example:
|
||||
# abc1234567890abcdef # repo-wide clang-format (no behavior change)
|
||||
#
|
||||
# To enable locally:
|
||||
# git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
@@ -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-----
|
||||
@@ -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())
|
||||
@@ -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)"
|
||||
@@ -2,7 +2,7 @@ name: Build All Platforms
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches: [master, cpp20-modernization]
|
||||
tags: ['v*']
|
||||
pull_request:
|
||||
branches: [master]
|
||||
@@ -14,13 +14,15 @@ jobs:
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential cmake ninja-build \
|
||||
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
||||
libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
librocksdb-dev libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
@@ -37,6 +39,52 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: cd build && ctest --output-on-failure || true
|
||||
|
||||
test-linux-sanitizers:
|
||||
# ASan + UBSan build of the daemon + unit tests. Allowed to fail until
|
||||
# findings are triaged — see .github/workflows/lint.yml comment block.
|
||||
# Once the test suite is clean under sanitizers, drop continue-on-error.
|
||||
runs-on: ubuntu-22.04
|
||||
continue-on-error: true
|
||||
env:
|
||||
# ASan: leak detection off by default (BDB and OpenSSL produce noise on shutdown).
|
||||
# Re-enable once we've quieted the legitimate suspects.
|
||||
ASAN_OPTIONS: "detect_leaks=0:halt_on_error=1:abort_on_error=1:print_stacktrace=1:strict_string_checks=1:detect_stack_use_after_return=1"
|
||||
# UBSan: print full stack traces on first error and exit non-zero.
|
||||
UBSAN_OPTIONS: "halt_on_error=1:abort_on_error=1:print_stacktrace=1"
|
||||
# Suppress UB categories that are pervasive in the Hash9 C cascade
|
||||
# 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:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential cmake ninja-build \
|
||||
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
||||
librocksdb-dev libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
|
||||
- name: Configure with sanitizers
|
||||
run: |
|
||||
cmake -B build-san -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_C_FLAGS="$SAN_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$SAN_FLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$SAN_FLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$SAN_FLAGS" \
|
||||
-DBUILD_QT=OFF \
|
||||
-DBUILD_DAEMON=ON \
|
||||
-DBUILD_TESTS=ON \
|
||||
-DUSE_UPNP=OFF
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build-san -j$(nproc)
|
||||
|
||||
- name: Run unit tests under sanitizers
|
||||
run: cd build-san && ctest --output-on-failure
|
||||
|
||||
build-windows-qt:
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
@@ -44,6 +92,8 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@@ -61,6 +111,7 @@ jobs:
|
||||
mingw-w64-x86_64-libevent
|
||||
mingw-w64-x86_64-miniupnpc
|
||||
mingw-w64-x86_64-zlib
|
||||
mingw-w64-x86_64-rocksdb
|
||||
|
||||
- name: Set VERSION
|
||||
run: |
|
||||
@@ -194,6 +245,8 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@@ -209,6 +262,7 @@ jobs:
|
||||
mingw-w64-x86_64-libevent
|
||||
mingw-w64-x86_64-miniupnpc
|
||||
mingw-w64-x86_64-zlib
|
||||
mingw-w64-x86_64-rocksdb
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
@@ -256,6 +310,8 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set VERSION
|
||||
run: |
|
||||
@@ -274,7 +330,7 @@ jobs:
|
||||
sudo apt-get install -y build-essential cmake ninja-build \
|
||||
qtbase5-dev qttools5-dev-tools \
|
||||
libboost-all-dev libssl-dev libdb++-dev \
|
||||
libleveldb-dev libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
libleveldb-dev librocksdb-dev libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
@@ -373,6 +429,8 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set VERSION
|
||||
run: |
|
||||
@@ -390,7 +448,7 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential cmake ninja-build \
|
||||
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
||||
libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
librocksdb-dev libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
@@ -503,6 +561,8 @@ jobs:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set VERSION
|
||||
run: |
|
||||
@@ -517,7 +577,7 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install cmake ninja qt@5 openssl@3 boost berkeley-db@5 leveldb libevent miniupnpc
|
||||
brew install cmake ninja qt@5 openssl@3 boost berkeley-db@5 leveldb rocksdb libevent miniupnpc
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
# Diff-only enforcement: clang-format and clang-tidy run only on lines changed
|
||||
# in the PR. Existing files keep their current style until they're edited.
|
||||
# See .clang-format and .clang-tidy for the rule sets.
|
||||
|
||||
jobs:
|
||||
clang-format-diff:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Need merge-base with target branch to compute the diff.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install clang-format
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-format-15
|
||||
sudo ln -sf /usr/bin/clang-format-15 /usr/local/bin/clang-format
|
||||
|
||||
- name: Check format on changed lines
|
||||
run: |
|
||||
BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD)
|
||||
echo "Comparing against merge-base: $BASE_SHA"
|
||||
|
||||
# git-clang-format prints a diff if any changed line violates style.
|
||||
# --diff exits non-zero when reformatting would change something.
|
||||
OUTPUT=$(git clang-format --diff "$BASE_SHA" -- '*.cpp' '*.h' '*.hpp' '*.cc' || true)
|
||||
|
||||
if [ -z "$OUTPUT" ] || [ "$OUTPUT" = "no modified files to format" ] || [ "$OUTPUT" = "clang-format did not modify any files" ]; then
|
||||
echo "clang-format: clean"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "::error::clang-format wants to change the following on lines you touched."
|
||||
echo "Run \`git clang-format $BASE_SHA\` locally and commit the result."
|
||||
echo "$OUTPUT"
|
||||
exit 1
|
||||
|
||||
clang-tidy-diff:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies + clang-tidy
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential cmake ninja-build clang-tidy-15 \
|
||||
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
||||
librocksdb-dev libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
sudo ln -sf /usr/bin/clang-tidy-15 /usr/local/bin/clang-tidy
|
||||
|
||||
- name: Configure (export compile_commands.json)
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DBUILD_QT=OFF \
|
||||
-DBUILD_DAEMON=ON \
|
||||
-DBUILD_TESTS=ON \
|
||||
-DUSE_UPNP=OFF
|
||||
|
||||
- name: Generate build artifacts that headers depend on
|
||||
# build.h, qt UI headers, etc. — clang-tidy needs them to parse sources.
|
||||
run: cmake --build build --target generate_build_info
|
||||
|
||||
- name: Run clang-tidy on changed lines
|
||||
run: |
|
||||
BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD)
|
||||
echo "Comparing against merge-base: $BASE_SHA"
|
||||
|
||||
# clang-tidy-diff.py ships with clang-tidy; runs tidy only on changed lines.
|
||||
DIFF_SCRIPT=$(dpkg -L clang-tidy-15 | grep clang-tidy-diff.py | head -1)
|
||||
if [ -z "$DIFF_SCRIPT" ]; then
|
||||
DIFF_SCRIPT=/usr/share/clang/clang-tidy-diff.py
|
||||
fi
|
||||
echo "Using: $DIFF_SCRIPT"
|
||||
|
||||
# -p1 strips the leading "a/"/"b/" from git diff paths.
|
||||
# -path=build points clang-tidy at compile_commands.json.
|
||||
# -iregex restricts to project sources (not vendored).
|
||||
git diff -U0 "$BASE_SHA" -- 'src/*.cpp' 'src/*.h' \
|
||||
':(exclude)src/json/nlohmann_json.hpp' \
|
||||
':(exclude)src/leveldb/*' \
|
||||
':(exclude)src/lz4/*' \
|
||||
':(exclude)src/tor/tor-src/*' \
|
||||
| python3 "$DIFF_SCRIPT" -p1 -path build \
|
||||
-iregex '.*\.(cpp|cc|h|hpp)$' \
|
||||
-j$(nproc) || EXIT=$?
|
||||
|
||||
# Warn-only initially. Flip this to `exit ${EXIT:-0}` once we're clean.
|
||||
exit 0
|
||||
@@ -1,3 +1,6 @@
|
||||
# Per-user Claude Code settings (machine-specific paths/permissions)
|
||||
.claude/
|
||||
|
||||
# Build artifacts
|
||||
*.o
|
||||
*.exe
|
||||
@@ -68,3 +71,10 @@ triangles.conf
|
||||
*.o
|
||||
src/trianglesd
|
||||
src/obj/
|
||||
build-bench/
|
||||
build-cmake/
|
||||
build-cmake-test/
|
||||
build-latest/
|
||||
build-rocks-probe/
|
||||
build-rocksdb/
|
||||
bench-results.csv
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
[submodule "src/tor/tor-src"]
|
||||
path = src/tor/tor-src
|
||||
url = https://gitlab.torproject.org/tpo/core/tor.git
|
||||
[submodule "src/secp256k1"]
|
||||
path = src/secp256k1
|
||||
url = https://github.com/bitcoin-core/secp256k1
|
||||
|
||||
@@ -6,13 +6,15 @@ if(POLICY CMP0167)
|
||||
endif()
|
||||
|
||||
project(Triangles
|
||||
VERSION 5.9.5
|
||||
VERSION 6.0.0
|
||||
DESCRIPTION "Cryptographic Triangles Wallet"
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
# ── C++ Standard ──
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
# C++20 required: RocksDB headers in MSYS2/Homebrew (8.x+) use `using enum`
|
||||
# and defaulted operator== on user-defined types, both C++20-only.
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
@@ -52,7 +54,6 @@ option(USE_QRCODE "Enable QR code generation via libqrencode" OFF
|
||||
option(USE_DBUS "Enable D-Bus notifications (Linux only)" ON)
|
||||
option(USE_ZMQ "Enable ZMQ publisher support" OFF)
|
||||
option(USE_TOR_EMBEDDED "Enable embedded Tor library linking" OFF)
|
||||
option(BUILD_ROCKSDB "Build with RocksDB chain database backend" OFF)
|
||||
option(USE_O3 "Use -O3 optimization instead of -O2" OFF)
|
||||
option(ENABLE_PIE "Build position-independent executables" OFF)
|
||||
option(ENABLE_STATIC "Prefer static linking (Linux release builds)" OFF)
|
||||
@@ -72,7 +73,7 @@ include(AddCompilerFlags)
|
||||
# ── Find required dependencies ──
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(Boost 1.71 REQUIRED COMPONENTS
|
||||
filesystem program_options thread chrono
|
||||
program_options thread chrono
|
||||
)
|
||||
if(BUILD_TESTS)
|
||||
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
|
||||
@@ -96,17 +97,65 @@ if(USE_ZMQ)
|
||||
pkg_check_modules(ZMQ REQUIRED IMPORTED_TARGET libzmq)
|
||||
endif()
|
||||
|
||||
if(BUILD_ROCKSDB)
|
||||
# RocksDB ships a CMake config package on most distros (rocksdbConfig.cmake).
|
||||
# On MSYS2/Homebrew/vcpkg the imported target is RocksDB::rocksdb.
|
||||
find_package(RocksDB CONFIG)
|
||||
if(NOT RocksDB_FOUND)
|
||||
# Fall back to pkg-config for systems without the CMake config (older
|
||||
# Linux distros). Builds an IMPORTED target named PkgConfig::RocksDB.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(RocksDB REQUIRED IMPORTED_TARGET rocksdb)
|
||||
# RocksDB is now a hard dependency: backs both the chain database and the
|
||||
# secure-messaging store (smessage). Probe in order:
|
||||
# 1. CMake config package (MSYS2, Homebrew, vcpkg, recent Linux)
|
||||
# 2. pkg-config (some Linux distros, no .cmake files)
|
||||
# 3. Manual find_path/find_library (Ubuntu 22.04's librocksdb-dev ships
|
||||
# neither a CMake config nor a .pc file)
|
||||
# In all paths, a target named RocksDB::rocksdb is exposed for consumers.
|
||||
find_package(RocksDB CONFIG QUIET)
|
||||
if(NOT RocksDB_FOUND)
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PkgConfig_FOUND)
|
||||
pkg_check_modules(RocksDB IMPORTED_TARGET QUIET rocksdb)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT TARGET RocksDB::rocksdb AND NOT TARGET PkgConfig::RocksDB)
|
||||
find_path(ROCKSDB_INCLUDE_DIR
|
||||
NAMES rocksdb/db.h
|
||||
PATHS /usr/include /usr/local/include
|
||||
)
|
||||
find_library(ROCKSDB_LIBRARY
|
||||
NAMES rocksdb
|
||||
PATHS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib
|
||||
)
|
||||
if(NOT ROCKSDB_INCLUDE_DIR OR NOT ROCKSDB_LIBRARY)
|
||||
message(FATAL_ERROR
|
||||
"RocksDB not found. Install librocksdb-dev (Ubuntu/Debian), "
|
||||
"rocksdb (Homebrew), or mingw-w64-x86_64-rocksdb (MSYS2).")
|
||||
endif()
|
||||
add_library(RocksDB::rocksdb UNKNOWN IMPORTED)
|
||||
set_target_properties(RocksDB::rocksdb PROPERTIES
|
||||
IMPORTED_LOCATION "${ROCKSDB_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${ROCKSDB_INCLUDE_DIR}"
|
||||
)
|
||||
message(STATUS "Found RocksDB (manual probe): ${ROCKSDB_LIBRARY}")
|
||||
endif()
|
||||
|
||||
# libsecp256k1 — vendored as a git submodule under src/secp256k1. Provides
|
||||
# ECDSA signing/verification, pubkey recovery (via the recovery module), and
|
||||
# ECDH for secure messaging. Configure the submodule's build for our needs:
|
||||
# only ECDH + recovery, none of the test/benchmark/extra-module bloat, and
|
||||
# don't install (we link statically against the in-tree target).
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/src/secp256k1/CMakeLists.txt")
|
||||
message(FATAL_ERROR
|
||||
"src/secp256k1 is empty. Run: git submodule update --init --recursive")
|
||||
endif()
|
||||
set(SECP256K1_DISABLE_SHARED ON CACHE INTERNAL "")
|
||||
set(SECP256K1_INSTALL OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_BUILD_BENCHMARK OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_BUILD_TESTS OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_BUILD_EXHAUSTIVE_TESTS OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_BUILD_CTIME_TESTS OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_BUILD_EXAMPLES OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_ENABLE_MODULE_ECDH ON CACHE INTERNAL "")
|
||||
set(SECP256K1_ENABLE_MODULE_RECOVERY ON CACHE INTERNAL "")
|
||||
set(SECP256K1_ENABLE_MODULE_EXTRAKEYS OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_ENABLE_MODULE_SCHNORRSIG OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_ENABLE_MODULE_MUSIG OFF CACHE INTERNAL "")
|
||||
set(SECP256K1_ENABLE_MODULE_ELLSWIFT OFF CACHE INTERNAL "")
|
||||
add_subdirectory(src/secp256k1 EXCLUDE_FROM_ALL)
|
||||
|
||||
if(BUILD_QT)
|
||||
find_package(Qt5 5.9 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
@@ -143,7 +192,6 @@ message(STATUS " QR code: ${USE_QRCODE}")
|
||||
message(STATUS " D-Bus: ${USE_DBUS}")
|
||||
message(STATUS " ZMQ: ${USE_ZMQ}")
|
||||
message(STATUS " Embedded Tor: ${USE_TOR_EMBEDDED}")
|
||||
message(STATUS " RocksDB backend: ${BUILD_ROCKSDB}")
|
||||
message(STATUS " Static linking: ${ENABLE_STATIC}")
|
||||
message(STATUS " ccache: ${CCACHE_PROGRAM}")
|
||||
message(STATUS " Unity build: ${ENABLE_UNITY_BUILD}")
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
# TRI v6 Development Task Queue
|
||||
|
||||
*Autonomous development pipeline — Krystie cycles through these continuously.*
|
||||
|
||||
## Legend
|
||||
- **P0** = Critical (chain broken / users blocked)
|
||||
- **P1** = Important (v6 milestone)
|
||||
- **P2** = Nice-to-have (polish / optimization)
|
||||
- **Status**: TODO | IN-PROGRESS | DONE | BLOCKED
|
||||
|
||||
---
|
||||
|
||||
## P0 — Immediate (Unblock Chain & Users)
|
||||
|
||||
### T001: Fix DNS2 RPC thread crash
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: ThreadRPCServer exits on bad auth attempts from external IPs. Need to not kill the RPC thread on individual auth failures.
|
||||
- **Files**: `src/rpc.cpp` or `src/bitcoinrpc.cpp`
|
||||
- **Acceptance**: RPC stays up even with bad auth attempts; curl JSON-RPC works reliably
|
||||
- **Model**: Claude Code or MiniMax M2.7
|
||||
|
||||
### T002: Fix DNS2 wallet 0 confirmed balance
|
||||
- **Status**: TODO
|
||||
- **Depends**: T001 (need reliable RPC)
|
||||
- **Description**: Wallet restored from April 20 backup. Shows 11.24 TRI unconfirmed. Need to verify rescan completes and coins mature (520 confirmations) for staking.
|
||||
- **Files**: wallet.dat, `src/wallet.cpp`
|
||||
- **Acceptance**: Wallet shows confirmed balance after rescan + confirmations
|
||||
- **Model**: Krystie (manual investigation, not subagent)
|
||||
|
||||
### T003: Fix seeds.txt parsing (only returns 1 address)
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: HTTPS fetch of seeds.cryptographic-triangles.org/seeds.txt only returns 1 address. Possible comment parsing bug in net.cpp seed fetch logic.
|
||||
- **Files**: `src/net.cpp`, `/var/www/seeds/seeds.txt`
|
||||
- **Acceptance**: All 7 onion addresses returned on fetch
|
||||
- **Model**: ZAI GLM-5.1
|
||||
|
||||
### T004: Fix Sami's PC wallet block 570 stall
|
||||
- **Status**: IN-PROGRESS
|
||||
- **Depends**: Windows binary build (DONE — built on sami-pc)
|
||||
- **Description**: Windows Qt wallet stuck at block 570. GUI bootstrap fix committed (d0fb2dc). New binary built at E:\repos\triangles_v5\build-mingw\bin\triangles-qt.exe. Needs testing.
|
||||
- **Acceptance**: Windows wallet syncs past block 570 with bootstrap
|
||||
- **Model**: Krystie (manual deployment)
|
||||
|
||||
---
|
||||
|
||||
## P1 — v6 Core Milestones
|
||||
|
||||
### T010: Complete RocksDB runtime testing
|
||||
- **Status**: TODO
|
||||
- **Depends**: T001
|
||||
- **Description**: RocksDB backend compiles clean but never tested with actual blockchain data. Need to: start daemon with `-rocksdb`, let it index chain, verify block lookups work, compare performance vs LevelDB.
|
||||
- **Files**: `src/txdb.h`, `src/txdb.cpp`, `src/utxosnapshot.cpp`
|
||||
- **Acceptance**: Daemon runs with `-rocksdb` flag, processes blocks, RPC queries return correct data
|
||||
- **Model**: MiniMax M2.7
|
||||
|
||||
### T011: Wire UTXO snapshot P2P distribution (SnapshotNet)
|
||||
- **Status**: TODO
|
||||
- **Depends**: T010
|
||||
- **Description**: `snapshotnet.cpp` exists but is placeholder. Need to implement: peer advertisement of snapshot availability, chunk transfer protocol, hash verification, integration with bootstrap flow.
|
||||
- **Files**: `src/snapshotnet.cpp`, `src/net.cpp`, `src/utxosnapshot.cpp`
|
||||
- **Acceptance**: New node can get UTXO snapshot from peers via P2P (not just HTTPS)
|
||||
- **Model**: Claude Code + MiniMax M2.7 (architecture + implementation)
|
||||
|
||||
### T012: Implement automated checkpoint generation (DESIGN DONE)
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: Checkpoints exist through block 2,207,000 but are manually maintained. Need automated checkpoint generation: every N blocks, compute checkpoint hash, push to code or external manifest.
|
||||
- **Files**: `src/checkpoints.cpp`, `src/checkpoints.h`
|
||||
- **Acceptance**: New checkpoints generated automatically, committed or published
|
||||
- **Model**: Claude Code
|
||||
|
||||
### T013: GPG signing for bootstrap artifacts
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: GPG key created (6913E13610F698183429CE20C2DC60618C85A159). Need to: sign every bootstrap/snapshot artifact on generation, verify signature on download, publish public key.
|
||||
- **Files**: `/usr/local/bin/auto-update.sh`, `src/bootstrap.cpp`
|
||||
- **Acceptance**: `gpg --verify` works on downloaded artifacts
|
||||
- **Model**: ZAI GLM-5.1
|
||||
|
||||
### T014: Contabo seed Docker image hardening
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: Seeds are running but image is fragile. Need: proper Dockerfile with version pinning, health checks, auto-restart, log shipping, and persistent volumes.
|
||||
- **Files**: `/tmp/Dockerfile` on Contabo, `/tri/seed-{1..4}/`
|
||||
- **Acceptance**: Seeds survive host reboot, auto-restart on crash, health check endpoint
|
||||
- **Model**: ZAI GLM-5.1
|
||||
|
||||
### T015: Network health dashboard
|
||||
- **Status**: TODO
|
||||
- **Depends**: T001, T003
|
||||
- **Description**: Operator-facing dashboard showing: block height per node, peer count, staking weight, chain sync status, seed health. Could be a simple web page served from DNS2.
|
||||
- **Files**: New — `src/rpcblockchain.cpp` (health endpoint), frontend
|
||||
- **Acceptance**: Live page showing all 7 nodes' status updated every 30s
|
||||
- **Model**: MiniMax M2.7 (design) + Claude Code (implementation)
|
||||
|
||||
### T016: Hetzner ARM64 persistent setup
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: Hetzner node is running but manually configured. Need: systemd service, auto-start on boot, bootstrap automation, monitoring.
|
||||
- **Files**: systemd unit file on Hetzner
|
||||
- **Acceptance**: Node survives reboot, auto-syncs, reports health
|
||||
- **Model**: Krystie (manual, it's infra not code)
|
||||
|
||||
---
|
||||
|
||||
## P2 — Polish & Optimization
|
||||
|
||||
### T020: Remove unused Gemini/Google references from codebase
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: Clean up any dead code, unused imports, stale comments referencing old architectures.
|
||||
- **Model**: ZAI GLM-5.1
|
||||
|
||||
### T021: Comprehensive test suite
|
||||
- **Status**: TODO
|
||||
- **Depends**: T010
|
||||
- **Description**: Expand test coverage for: UTXO snapshot load/dump, RocksDB backend, bootstrap download, seed fetch, checkpoint verification.
|
||||
- **Files**: `src/test/`
|
||||
- **Acceptance**: `test_triangles` passes with < 5 pre-existing failures
|
||||
- **Model**: ZAI GLM-5.1 + MiniMax M2.7
|
||||
|
||||
### T022: CI/CD pipeline for releases
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: GitHub Actions workflow: on tag push, build Linux x86_64 + ARM64 + Windows, create release with all binaries + checksums.
|
||||
- **Files**: `.github/workflows/build-all.yml`
|
||||
- **Acceptance**: Tag push produces release with 3 platform binaries
|
||||
- **Model**: ZAI GLM-5.1
|
||||
|
||||
### T023: TRIdock + tri-wallet-web consolidation
|
||||
- **Status**: TODO
|
||||
- **Depends**: none
|
||||
- **Description**: TRIdock and tri-wallet-web appear to be near-duplicates. Evaluate and either consolidate or clearly separate concerns.
|
||||
- **Model**: MiniMax M2.7 (analysis)
|
||||
|
||||
---
|
||||
|
||||
## Completed
|
||||
|
||||
### ✅ Windows GUI bootstrap fix (d0fb2dc)
|
||||
- Removed `#ifndef QT_GUI` guard so auto-bootstrap runs in GUI wallet
|
||||
- Added `uiInterface.InitMessage()` for progress display
|
||||
|
||||
### ✅ Windows native build on sami-pc
|
||||
- Built `triangles-qt.exe` (26MB) and `trianglesd.exe` via MSYS2/MinGW64
|
||||
- All dependencies found natively
|
||||
|
||||
### ✅ RocksDB integration complete (ac9c6fb)
|
||||
- CActiveTxDB wrapper, dual-backend support, compiles clean
|
||||
|
||||
### ✅ All nodes updated to v5.9.7.0
|
||||
- DNS2, DNS3, Hetzner, Contabo seeds all running latest
|
||||
|
||||
### ✅ Bootstrap infrastructure live
|
||||
- HTTPS at bootstrap.cryptographic-triangles.org
|
||||
- Tor hidden service serving nginx on port 8085
|
||||
- Seeds.txt with 7 onion nodes
|
||||
@@ -9,6 +9,20 @@ if(NOT TARGET leveldb_lib)
|
||||
add_subdirectory("${LEVELDB_SOURCE_DIR}" "${LEVELDB_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
# Pin bundled LevelDB to C++17. It only needs C++11 (declared via its own
|
||||
# target_compile_features) but inherits CMAKE_CXX_STANDARD=20 from the
|
||||
# top-level project, where some of its atomic-enum syntax
|
||||
# (std::memory_order::memory_order_relaxed) becomes a hard error.
|
||||
foreach(_leveldb_target leveldb_lib leveldb_memenv)
|
||||
if(TARGET ${_leveldb_target})
|
||||
set_target_properties(${_leveldb_target} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT TARGET build_leveldb)
|
||||
add_custom_target(build_leveldb DEPENDS leveldb_lib leveldb_memenv)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Chain DB benchmark harness
|
||||
|
||||
Measures `FastImportBlockFile()` speed under each chain-DB backend
|
||||
(LevelDB vs RocksDB) using a user-supplied `blk0001.dat` block stream.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A `trianglesd` binary (RocksDB is now a hard build dep, both backends are
|
||||
always available):
|
||||
```
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_QT=OFF \
|
||||
-DBUILD_DAEMON=ON
|
||||
cmake --build build
|
||||
```
|
||||
- An `blk0001.dat` file (old-style block stream). If you have a synced
|
||||
node, copy `~/.triangles/blk0001.dat` (Linux) or `%APPDATA%\triangles\blk0001.dat` (Windows).
|
||||
- Free disk space: ~3× the size of `blk0001.dat` per backend run
|
||||
(raw blocks + chain DB index + working space).
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
contrib/bench/bench-chaindb.sh \
|
||||
--binary=$(pwd)/build/bin/trianglesd \
|
||||
--bootstrap=/path/to/blk0001.dat
|
||||
```
|
||||
|
||||
Runs each backend in turn, appends a CSV row to `./bench-results.csv`,
|
||||
and prints a summary to stdout. Default `--dbcache=2048` (MB).
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Default | Notes |
|
||||
| --- | --- | --- |
|
||||
| `--binary=PATH` | (required) | Path to `trianglesd` |
|
||||
| `--bootstrap=PATH` | (required) | Path to `blk0001.dat` |
|
||||
| `--backends=LIST` | `leveldb,rocksdb` | Comma-separated subset |
|
||||
| `--workdir=DIR` | `/tmp/triangles-bench-XXXXXX` | Per-backend datadirs go here |
|
||||
| `--dbcache=MB` | `2048` | Chain DB cache size |
|
||||
| `--results-csv=FILE` | `./bench-results.csv` | Appended to |
|
||||
| `--keep-datadirs` | off | Preserve datadirs after run for inspection |
|
||||
| `--rpc-port=BASE` | `19112` | Each backend uses `BASE+offset` |
|
||||
|
||||
## What it measures
|
||||
|
||||
| Column | Source |
|
||||
| --- | --- |
|
||||
| `wall_ms` | The daemon's own log line: `FastImportBlockFile: indexed N blocks in Mms` |
|
||||
| `peak_rss_kb` | `ps -o rss=` sampled once per second |
|
||||
| `datadir_bytes` | `du -sb` of the working datadir (includes `blk0001.dat`) |
|
||||
| `blocks_indexed` | Parsed from the same log line |
|
||||
|
||||
## What it does not measure
|
||||
|
||||
- Network IBD (peer fetch, header sync) — this is pure DB ingest.
|
||||
- UTXO snapshot load — `LoadSnapshot` is currently rocksdb-guarded
|
||||
(see `src/utxosnapshot.cpp`); will be unblocked when LevelDB is retired.
|
||||
- Reorg cost — separate test, not yet implemented.
|
||||
- Disk I/O bytes (read/written) — could be added with `iostat` integration.
|
||||
|
||||
## Interpreting results
|
||||
|
||||
A meaningful comparison requires both rows to have run on the same machine
|
||||
with the same `blk0001.dat`. The `host` column makes mixing runs across
|
||||
machines visible in the CSV.
|
||||
|
||||
Backend-relevant size comparisons should subtract `bootstrap_size_bytes`
|
||||
from `datadir_bytes` to isolate the chain DB tree.
|
||||
|
||||
## One-liners
|
||||
|
||||
```bash
|
||||
# LevelDB only
|
||||
./bench-chaindb.sh --binary=... --bootstrap=... --backends=leveldb
|
||||
|
||||
# Compare 2GB vs 4GB cache on RocksDB
|
||||
./bench-chaindb.sh --binary=... --bootstrap=... --backends=rocksdb --dbcache=2048
|
||||
./bench-chaindb.sh --binary=... --bootstrap=... --backends=rocksdb --dbcache=4096
|
||||
|
||||
# Keep the datadirs for poking around afterwards
|
||||
./bench-chaindb.sh --binary=... --bootstrap=... --keep-datadirs
|
||||
```
|
||||
@@ -0,0 +1,217 @@
|
||||
#!/usr/bin/env bash
|
||||
# Benchmark FastImportBlockFile() speed across chain-DB backends.
|
||||
#
|
||||
# Reads a user-supplied blk0001.dat (old-style block stream) and times the
|
||||
# full block-index rebuild under each backend. Output: a CSV row per backend
|
||||
# with wall time, peak RSS, and resulting datadir size on disk.
|
||||
#
|
||||
# Usage:
|
||||
# ./bench-chaindb.sh \
|
||||
# --binary=/path/to/trianglesd \
|
||||
# --bootstrap=/path/to/blk0001.dat \
|
||||
# [--backends=leveldb,rocksdb] default: both
|
||||
# [--workdir=/tmp/triangles-bench] parent dir for per-backend datadirs
|
||||
# [--dbcache=2048] in MB
|
||||
# [--results-csv=./bench-results.csv]
|
||||
# [--keep-datadirs] preserve datadirs after run
|
||||
# [--rpc-port=BASE] default 19112; each run uses BASE+offset
|
||||
#
|
||||
# Notes:
|
||||
# - RocksDB is a hard build dep, so any current trianglesd has both backends.
|
||||
# - This script does not assume Tor is configured. It launches with -nolisten
|
||||
# and -connect=0 to keep the run network-isolated.
|
||||
# - Wall time comes from the daemon's own perf log line:
|
||||
# "FastImportBlockFile: indexed N blocks in Mms"
|
||||
# - Peak RSS is sampled via `ps -o rss=` once a second.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ────────────────────────────────────────────────────────────────
|
||||
BINARY=""
|
||||
BOOTSTRAP=""
|
||||
BACKENDS="leveldb,rocksdb"
|
||||
WORKDIR=""
|
||||
DBCACHE=2048
|
||||
RESULTS_CSV="./bench-results.csv"
|
||||
KEEP=0
|
||||
RPC_BASE=19112
|
||||
|
||||
# ── Arg parsing ─────────────────────────────────────────────────────────────
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--binary=*) BINARY="${arg#*=}" ;;
|
||||
--bootstrap=*) BOOTSTRAP="${arg#*=}" ;;
|
||||
--backends=*) BACKENDS="${arg#*=}" ;;
|
||||
--workdir=*) WORKDIR="${arg#*=}" ;;
|
||||
--dbcache=*) DBCACHE="${arg#*=}" ;;
|
||||
--results-csv=*) RESULTS_CSV="${arg#*=}" ;;
|
||||
--keep-datadirs) KEEP=1 ;;
|
||||
--rpc-port=*) RPC_BASE="${arg#*=}" ;;
|
||||
-h|--help)
|
||||
sed -n '2,28p' "$0" | sed 's/^# \?//'
|
||||
exit 0 ;;
|
||||
*)
|
||||
echo "Unknown argument: $arg" >&2
|
||||
exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$BINARY" ] || { echo "--binary is required" >&2; exit 2; }
|
||||
[ -n "$BOOTSTRAP" ] || { echo "--bootstrap is required" >&2; exit 2; }
|
||||
[ -x "$BINARY" ] || { echo "Binary not executable: $BINARY" >&2; exit 2; }
|
||||
[ -f "$BOOTSTRAP" ] || { echo "Bootstrap file not found: $BOOTSTRAP" >&2; exit 2; }
|
||||
|
||||
if [ -z "$WORKDIR" ]; then
|
||||
WORKDIR="$(mktemp -d -t triangles-bench-XXXXXX)"
|
||||
fi
|
||||
mkdir -p "$WORKDIR"
|
||||
echo "Workdir: $WORKDIR"
|
||||
|
||||
# ── CSV header (only if file is new) ───────────────────────────────────────
|
||||
if [ ! -f "$RESULTS_CSV" ]; then
|
||||
echo "timestamp,backend,bootstrap_size_bytes,dbcache_mb,blocks_indexed,wall_ms,peak_rss_kb,datadir_bytes,binary,host" > "$RESULTS_CSV"
|
||||
fi
|
||||
|
||||
bootstrap_size="$(stat -c%s "$BOOTSTRAP" 2>/dev/null || stat -f%z "$BOOTSTRAP")"
|
||||
host="$(hostname)"
|
||||
ts_run="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
|
||||
# ── Per-backend run ─────────────────────────────────────────────────────────
|
||||
run_backend() {
|
||||
local backend="$1"
|
||||
local idx="$2"
|
||||
local datadir="$WORKDIR/$backend"
|
||||
local rpc_port=$((RPC_BASE + idx))
|
||||
local rss_log="$WORKDIR/$backend.rss.log"
|
||||
|
||||
echo
|
||||
echo "════════════════════════════════════════════════════════════════════"
|
||||
echo " Backend: $backend (datadir: $datadir, rpcport: $rpc_port)"
|
||||
echo "════════════════════════════════════════════════════════════════════"
|
||||
|
||||
# Fresh datadir, copy bootstrap into place. FastImportBlockFile() picks
|
||||
# this up automatically when the block index is empty.
|
||||
rm -rf "$datadir"
|
||||
mkdir -p "$datadir"
|
||||
cp "$BOOTSTRAP" "$datadir/blk0001.dat"
|
||||
|
||||
# Minimal config — disable network so we measure only the import path.
|
||||
cat > "$datadir/triangles.conf" <<EOF
|
||||
chaindb=$backend
|
||||
dbcache=$DBCACHE
|
||||
nolisten=1
|
||||
connect=0
|
||||
rpcuser=bench
|
||||
rpcpassword=bench
|
||||
rpcport=$rpc_port
|
||||
debug=1
|
||||
printtoconsole=0
|
||||
EOF
|
||||
|
||||
# Launch in background. -daemon would daemonize but we want to track the
|
||||
# process tree; run in foreground and background it ourselves so we keep
|
||||
# the PID for RSS sampling and clean shutdown.
|
||||
local pid
|
||||
"$BINARY" -datadir="$datadir" -conf="triangles.conf" >"$datadir/stdout.log" 2>&1 &
|
||||
pid=$!
|
||||
echo "Launched $backend (pid $pid)"
|
||||
|
||||
# RSS sampler: log peak every second to a file.
|
||||
(
|
||||
while kill -0 "$pid" 2>/dev/null; do
|
||||
ps -o rss= -p "$pid" 2>/dev/null | tr -d ' ' >> "$rss_log" || true
|
||||
sleep 1
|
||||
done
|
||||
) &
|
||||
local sampler_pid=$!
|
||||
|
||||
# Watch for "FastImportBlockFile: indexed N blocks in Mms" in the daemon's
|
||||
# debug.log, which is the deterministic completion signal.
|
||||
local debug_log="$datadir/debug.log"
|
||||
local wait_start
|
||||
wait_start="$(date +%s)"
|
||||
local timeout_s=86400 # 24 hours hard cap
|
||||
local indexed_line=""
|
||||
while :; do
|
||||
if [ -f "$debug_log" ]; then
|
||||
indexed_line="$(grep -E "FastImportBlockFile: indexed [0-9]+ blocks in [0-9]+ms" "$debug_log" | tail -1 || true)"
|
||||
if [ -n "$indexed_line" ]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
if ! kill -0 "$pid" 2>/dev/null; then
|
||||
echo "Daemon exited before completion line appeared. Check $datadir/stdout.log" >&2
|
||||
kill "$sampler_pid" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
local elapsed=$(( $(date +%s) - wait_start ))
|
||||
if [ "$elapsed" -gt "$timeout_s" ]; then
|
||||
echo "Timeout after ${timeout_s}s without completion line" >&2
|
||||
kill "$pid" 2>/dev/null || true
|
||||
kill "$sampler_pid" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo "Completion: $indexed_line"
|
||||
|
||||
# Parse blocks_indexed and wall_ms from the line.
|
||||
local blocks_indexed wall_ms
|
||||
blocks_indexed="$(echo "$indexed_line" | sed -E 's/.*indexed ([0-9]+) blocks.*/\1/')"
|
||||
wall_ms="$(echo "$indexed_line" | sed -E 's/.*in ([0-9]+)ms.*/\1/')"
|
||||
|
||||
# Stop daemon cleanly via RPC, fall back to SIGTERM.
|
||||
"$BINARY" -datadir="$datadir" -conf="triangles.conf" stop >/dev/null 2>&1 || \
|
||||
kill -TERM "$pid" 2>/dev/null || true
|
||||
|
||||
# Wait up to 60s for clean exit.
|
||||
local stop_wait=0
|
||||
while kill -0 "$pid" 2>/dev/null && [ "$stop_wait" -lt 60 ]; do
|
||||
sleep 1
|
||||
stop_wait=$((stop_wait + 1))
|
||||
done
|
||||
kill -KILL "$pid" 2>/dev/null || true
|
||||
wait "$sampler_pid" 2>/dev/null || true
|
||||
|
||||
# Peak RSS: max of the sampler's recorded values.
|
||||
local peak_rss_kb=0
|
||||
if [ -f "$rss_log" ] && [ -s "$rss_log" ]; then
|
||||
peak_rss_kb="$(sort -nr "$rss_log" | head -1)"
|
||||
fi
|
||||
|
||||
# Datadir size — separate the chain DB from blk0001.dat (which is ~constant
|
||||
# across backends). We report the total datadir size; the consumer can
|
||||
# subtract bootstrap_size_bytes if they want chain-DB-only.
|
||||
local datadir_bytes
|
||||
datadir_bytes="$(du -sb "$datadir" 2>/dev/null | awk '{print $1}' || du -sk "$datadir" | awk '{print $1*1024}')"
|
||||
|
||||
# Append CSV row.
|
||||
echo "$ts_run,$backend,$bootstrap_size,$DBCACHE,$blocks_indexed,$wall_ms,$peak_rss_kb,$datadir_bytes,$BINARY,$host" >> "$RESULTS_CSV"
|
||||
|
||||
# Stdout summary.
|
||||
printf " blocks indexed: %s\n" "$blocks_indexed"
|
||||
printf " wall time: %s ms (%.1f min)\n" "$wall_ms" "$(awk "BEGIN{print $wall_ms/60000}")"
|
||||
printf " peak RSS: %s KB (%.1f GB)\n" "$peak_rss_kb" "$(awk "BEGIN{print $peak_rss_kb/1024/1024}")"
|
||||
printf " datadir size: %s bytes (%.1f GB)\n" "$datadir_bytes" "$(awk "BEGIN{print $datadir_bytes/1024/1024/1024}")"
|
||||
|
||||
# Cleanup unless --keep-datadirs.
|
||||
if [ "$KEEP" -eq 0 ]; then
|
||||
rm -rf "$datadir"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Main loop ──────────────────────────────────────────────────────────────
|
||||
idx=0
|
||||
IFS=',' read -r -a backends_arr <<< "$BACKENDS"
|
||||
for backend in "${backends_arr[@]}"; do
|
||||
case "$backend" in
|
||||
leveldb|rocksdb) ;;
|
||||
*) echo "Unknown backend: $backend" >&2; exit 2 ;;
|
||||
esac
|
||||
run_backend "$backend" "$idx"
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Done. Results appended to $RESULTS_CSV"
|
||||
@@ -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
|
||||
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Fresh-datadir IBD smoke test for TRI.
|
||||
# Goal: detect the classic "starts from zero but stalls early / loops around 570"
|
||||
# failure mode, and verify that sync keeps making forward progress.
|
||||
#
|
||||
# Example:
|
||||
# bash scripts/ibd-smoke-test.sh \
|
||||
# --bin ./build/src/trianglesd \
|
||||
# --bootstrap-url http://100.104.4.5:8085/triangles-bootstrap.tar.gz \
|
||||
# --addnode 74.208.167.19 --addnode 194.233.88.206
|
||||
|
||||
BIN="${BIN:-./build/src/trianglesd}"
|
||||
TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-1800}" # 30 minutes target window
|
||||
POLL_SECONDS="${POLL_SECONDS:-15}"
|
||||
STALL_WINDOW_SECONDS="${STALL_WINDOW_SECONDS:-180}"
|
||||
BOOTSTRAP_URL="${BOOTSTRAP_URL:-}"
|
||||
WORKDIR="${WORKDIR:-}"
|
||||
RPC_PORT="${RPC_PORT:-19192}"
|
||||
P2P_PORT="${P2P_PORT:-24193}"
|
||||
MIN_EXPECTED_HEIGHT="${MIN_EXPECTED_HEIGHT:-5000}"
|
||||
ALLOW_IBD="${ALLOW_IBD:-0}"
|
||||
WHITELIST="${WHITELIST:-127.0.0.1}"
|
||||
ADDNODES=()
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [options]
|
||||
|
||||
Options:
|
||||
--bin PATH trianglesd binary (default: $BIN)
|
||||
--bootstrap-url URL optional bootstrap tar.gz URL to preload
|
||||
--workdir PATH use an explicit temp workdir
|
||||
--rpc-port N RPC port for test node (default: $RPC_PORT)
|
||||
--p2p-port N P2P port for test node (default: $P2P_PORT)
|
||||
--timeout N total test timeout seconds (default: $TIMEOUT_SECONDS)
|
||||
--poll N poll interval seconds (default: $POLL_SECONDS)
|
||||
--stall-window N no-progress failure window seconds (default: $STALL_WINDOW_SECONDS)
|
||||
--min-height N minimum expected height/progress floor (default: $MIN_EXPECTED_HEIGHT)
|
||||
--allow-ibd allow test to pass while still in IBD if progress is strong
|
||||
--addnode HOST trusted peer to add (repeatable)
|
||||
-h, --help show this help
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--bin) BIN="$2"; shift 2 ;;
|
||||
--bootstrap-url) BOOTSTRAP_URL="$2"; shift 2 ;;
|
||||
--workdir) WORKDIR="$2"; shift 2 ;;
|
||||
--rpc-port) RPC_PORT="$2"; shift 2 ;;
|
||||
--p2p-port) P2P_PORT="$2"; shift 2 ;;
|
||||
--timeout) TIMEOUT_SECONDS="$2"; shift 2 ;;
|
||||
--poll) POLL_SECONDS="$2"; shift 2 ;;
|
||||
--stall-window) STALL_WINDOW_SECONDS="$2"; shift 2 ;;
|
||||
--min-height) MIN_EXPECTED_HEIGHT="$2"; shift 2 ;;
|
||||
--allow-ibd) ALLOW_IBD=1; shift ;;
|
||||
--addnode) ADDNODES+=("$2"); shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "unknown arg: $1" >&2; usage; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! -x "$BIN" ]]; then
|
||||
echo "ERROR: trianglesd binary not executable: $BIN" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ -z "$WORKDIR" ]]; then
|
||||
WORKDIR="$(mktemp -d /tmp/tri-ibd-smoke-XXXXXX)"
|
||||
fi
|
||||
DATADIR="$WORKDIR/datadir"
|
||||
mkdir -p "$DATADIR"
|
||||
|
||||
RPCUSER="tri_test"
|
||||
RPCPASSWORD="tri_test_$(date +%s)_$RANDOM"
|
||||
CONF="$DATADIR/triangles.conf"
|
||||
cat > "$CONF" <<EOF
|
||||
server=1
|
||||
daemon=1
|
||||
staking=0
|
||||
listen=1
|
||||
discover=0
|
||||
upnp=0
|
||||
tor=0
|
||||
irc=0
|
||||
dnsseed=1
|
||||
checkpoints=1
|
||||
rpcuser=$RPCUSER
|
||||
rpcpassword=$RPCPASSWORD
|
||||
rpcport=$RPC_PORT
|
||||
port=$P2P_PORT
|
||||
maxconnections=32
|
||||
whitelist=$WHITELIST
|
||||
logtimestamps=1
|
||||
EOF
|
||||
|
||||
for host in "${ADDNODES[@]}"; do
|
||||
echo "addnode=$host" >> "$CONF"
|
||||
done
|
||||
|
||||
cleanup() {
|
||||
"$BIN" -datadir="$DATADIR" -conf="$CONF" stop >/dev/null 2>&1 || true
|
||||
sleep 2 || true
|
||||
pkill -f "$DATADIR" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ -n "$BOOTSTRAP_URL" ]]; then
|
||||
echo "[ibd-test] downloading bootstrap: $BOOTSTRAP_URL"
|
||||
curl -L --fail --max-time 1800 "$BOOTSTRAP_URL" -o "$WORKDIR/bootstrap.tar.gz"
|
||||
tar xzf "$WORKDIR/bootstrap.tar.gz" -C "$DATADIR"
|
||||
rm -f "$DATADIR/database/log."* "$DATADIR/txleveldb/LOCK" "$DATADIR/smsgDB/LOCK" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "[ibd-test] starting node from datadir: $DATADIR"
|
||||
"$BIN" -daemon -datadir="$DATADIR" -conf="$CONF" >/dev/null
|
||||
sleep 6
|
||||
|
||||
rpc() {
|
||||
local method="$1"
|
||||
local params="${2:-[]}"
|
||||
curl -sS --fail --user "$RPCUSER:$RPCPASSWORD" \
|
||||
--data-binary "{\"jsonrpc\":\"1.0\",\"id\":\"ibd\",\"method\":\"$method\",\"params\":$params}" \
|
||||
-H 'content-type: text/plain;' "http://127.0.0.1:$RPC_PORT/"
|
||||
}
|
||||
|
||||
extract_json() {
|
||||
python3 -c 'import json,sys; obj=json.load(sys.stdin); print(obj["result"])'
|
||||
}
|
||||
|
||||
extract_field() {
|
||||
local field="$1"
|
||||
python3 -c 'import json,sys; obj=json.load(sys.stdin); val=obj["result"].get(sys.argv[1]); print(val if val is not None else "")' "$field"
|
||||
}
|
||||
|
||||
start_ts=$(date +%s)
|
||||
last_progress_ts=$start_ts
|
||||
last_height=-1
|
||||
samples=0
|
||||
same_570_loops=0
|
||||
best_height=0
|
||||
|
||||
while true; do
|
||||
now=$(date +%s)
|
||||
elapsed=$((now - start_ts))
|
||||
if (( elapsed > TIMEOUT_SECONDS )); then
|
||||
echo "FAIL: timeout after ${elapsed}s"
|
||||
break
|
||||
fi
|
||||
|
||||
if info_json="$(rpc getblockchaininfo 2>/dev/null)"; then
|
||||
height=$(printf '%s' "$info_json" | extract_field blocks)
|
||||
ibd=$(printf '%s' "$info_json" | extract_field initialblockdownload)
|
||||
headers=$(printf '%s' "$info_json" | extract_field headers)
|
||||
else
|
||||
height=""
|
||||
ibd=""
|
||||
headers=""
|
||||
fi
|
||||
|
||||
peers=0
|
||||
if peer_json="$(rpc getconnectioncount 2>/dev/null)"; then
|
||||
peers=$(printf '%s' "$peer_json" | extract_json)
|
||||
fi
|
||||
|
||||
if [[ -n "$height" && "$height" != "$last_height" ]]; then
|
||||
last_progress_ts=$now
|
||||
last_height="$height"
|
||||
if (( height > best_height )); then
|
||||
best_height=$height
|
||||
fi
|
||||
fi
|
||||
|
||||
log_file="$DATADIR/debug.log"
|
||||
if [[ -f "$log_file" ]]; then
|
||||
loop_hits=$(tail -n 400 "$log_file" | grep -c 'start=571' || true)
|
||||
if (( loop_hits >= 3 )); then
|
||||
same_570_loops=$loop_hits
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[ibd-test] t=${elapsed}s height=${height:-?} headers=${headers:-?} ibd=${ibd:-?} peers=$peers best=$best_height"
|
||||
|
||||
if [[ -n "$height" ]] && (( best_height >= MIN_EXPECTED_HEIGHT )) && [[ "$ibd" == "False" || "$ibd" == "false" ]]; then
|
||||
echo "PASS: left IBD and reached height $best_height"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$ALLOW_IBD" == "1" && -n "$height" ]] && (( best_height >= MIN_EXPECTED_HEIGHT )); then
|
||||
echo "PASS: strong sync progress observed (height $best_height) even though IBD remains true"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if (( now - last_progress_ts > STALL_WINDOW_SECONDS )); then
|
||||
echo "FAIL: no block-height progress for $((now - last_progress_ts))s"
|
||||
if (( same_570_loops > 0 )); then
|
||||
echo "HINT: detected repeated start=571 loop pattern ($same_570_loops hits in recent log tail)"
|
||||
fi
|
||||
echo "--- debug tail ---"
|
||||
tail -n 120 "$log_file" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
((samples++)) || true
|
||||
sleep "$POLL_SECONDS"
|
||||
done
|
||||
|
||||
exit 1
|
||||
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 17 KiB |
@@ -38,13 +38,14 @@ target_include_directories(json_compat INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/js
|
||||
# EXCLUDES init.cpp, wallet.cpp (QT_GUI-conditional), noui.cpp (target-specific)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
set(CORE_SOURCES
|
||||
alert.cpp
|
||||
addrman.cpp
|
||||
bootstrap.cpp
|
||||
checkpoints.cpp
|
||||
crypter.cpp
|
||||
hdwallet.cpp
|
||||
crypto_ecdh.cpp
|
||||
crypto_ecdsa.cpp
|
||||
db.cpp
|
||||
irc.cpp
|
||||
key.cpp
|
||||
keystore.cpp
|
||||
main.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
|
||||
@@ -74,6 +77,7 @@ set(CORE_SOURCES
|
||||
rpcsmessage.cpp
|
||||
zmqpublishnotifier.cpp
|
||||
txdb-base.cpp
|
||||
txdb-factory.cpp
|
||||
txdb-leveldb.cpp
|
||||
utxosnapshot.cpp
|
||||
snapshotnet.cpp
|
||||
@@ -98,10 +102,9 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM")
|
||||
list(APPEND CORE_SOURCES scrypt-arm.S)
|
||||
endif()
|
||||
|
||||
# Optional: RocksDB chain database backend
|
||||
if(BUILD_ROCKSDB)
|
||||
list(APPEND CORE_SOURCES txdb-rocksdb.cpp)
|
||||
endif()
|
||||
# RocksDB chain database backend (always built; see top-level CMakeLists.txt
|
||||
# for the rationale — RocksDB also backs the smessage store).
|
||||
list(APPEND CORE_SOURCES txdb-rocksdb.cpp)
|
||||
|
||||
add_library(triangles_common OBJECT ${CORE_SOURCES})
|
||||
|
||||
@@ -120,7 +123,6 @@ target_link_libraries(triangles_common PUBLIC
|
||||
leveldb_bundled
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
Boost::filesystem
|
||||
Boost::program_options
|
||||
Boost::thread
|
||||
Boost::chrono
|
||||
@@ -150,14 +152,15 @@ if(USE_ZMQ)
|
||||
target_link_libraries(triangles_common PUBLIC PkgConfig::ZMQ)
|
||||
endif()
|
||||
|
||||
# Optional: RocksDB
|
||||
if(BUILD_ROCKSDB)
|
||||
target_compile_definitions(triangles_common PUBLIC BUILD_ROCKSDB)
|
||||
if(TARGET RocksDB::rocksdb)
|
||||
target_link_libraries(triangles_common PUBLIC RocksDB::rocksdb)
|
||||
elseif(TARGET PkgConfig::RocksDB)
|
||||
target_link_libraries(triangles_common PUBLIC PkgConfig::RocksDB)
|
||||
endif()
|
||||
# libsecp256k1 (mandatory) — ECDH / ECDSA replacement for OpenSSL EC.
|
||||
# Provided by add_subdirectory(src/secp256k1) in the top-level CMakeLists.
|
||||
target_link_libraries(triangles_common PUBLIC secp256k1)
|
||||
|
||||
# RocksDB (mandatory)
|
||||
if(TARGET RocksDB::rocksdb)
|
||||
target_link_libraries(triangles_common PUBLIC RocksDB::rocksdb)
|
||||
elseif(TARGET PkgConfig::RocksDB)
|
||||
target_link_libraries(triangles_common PUBLIC PkgConfig::RocksDB)
|
||||
endif()
|
||||
|
||||
# Optional: Embedded Tor
|
||||
@@ -217,11 +220,11 @@ target_precompile_headers(triangles_common PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<cstring$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<memory$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<functional$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/filesystem.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/filesystem/fstream.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/thread.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/thread/mutex.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/thread/condition_variable.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<filesystem$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<fstream$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<thread$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<mutex$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<condition_variable$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/algorithm/string.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<openssl/sha.h$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<openssl/crypto.h$<ANGLE-R>>"
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "addrman.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int CAddrInfo::GetTriedBucket(const std::vector<unsigned char> &nKey) const
|
||||
@@ -79,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)
|
||||
@@ -175,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++;
|
||||
}
|
||||
@@ -438,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)
|
||||
{
|
||||
|
||||
@@ -465,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
//
|
||||
// Alert system
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <map>
|
||||
|
||||
#include "alert.h"
|
||||
#include "key.h"
|
||||
#include "net.h"
|
||||
#include "sync.h"
|
||||
#include "ui_interface.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
map<uint256, CAlert> mapAlerts;
|
||||
CCriticalSection cs_mapAlerts;
|
||||
|
||||
// Alert keys disabled for decentralization - v5 hard fork
|
||||
static const char* pszMainKey = "";
|
||||
|
||||
// TestNet alerts pubKey
|
||||
static const char* pszTestKey = "";
|
||||
|
||||
void CUnsignedAlert::SetNull()
|
||||
{
|
||||
nVersion = 1;
|
||||
nRelayUntil = 0;
|
||||
nExpiration = 0;
|
||||
nID = 0;
|
||||
nCancel = 0;
|
||||
setCancel.clear();
|
||||
nMinVer = 0;
|
||||
nMaxVer = 0;
|
||||
setSubVer.clear();
|
||||
nPriority = 0;
|
||||
|
||||
strComment.clear();
|
||||
strStatusBar.clear();
|
||||
strReserved.clear();
|
||||
}
|
||||
|
||||
std::string CUnsignedAlert::ToString() const
|
||||
{
|
||||
std::string strSetCancel;
|
||||
for (int n : setCancel)
|
||||
strSetCancel += strprintf("%d ", n);
|
||||
std::string strSetSubVer;
|
||||
for (std::string str : setSubVer)
|
||||
strSetSubVer += "\"" + str + "\" ";
|
||||
return strprintf(
|
||||
"CAlert(\n"
|
||||
" nVersion = %d\n"
|
||||
" nRelayUntil = %" PRId64 "\n"
|
||||
" nExpiration = %" PRId64 "\n"
|
||||
" nID = %d\n"
|
||||
" nCancel = %d\n"
|
||||
" setCancel = %s\n"
|
||||
" nMinVer = %d\n"
|
||||
" nMaxVer = %d\n"
|
||||
" setSubVer = %s\n"
|
||||
" nPriority = %d\n"
|
||||
" strComment = \"%s\"\n"
|
||||
" strStatusBar = \"%s\"\n"
|
||||
")\n",
|
||||
nVersion,
|
||||
nRelayUntil,
|
||||
nExpiration,
|
||||
nID,
|
||||
nCancel,
|
||||
strSetCancel.c_str(),
|
||||
nMinVer,
|
||||
nMaxVer,
|
||||
strSetSubVer.c_str(),
|
||||
nPriority,
|
||||
strComment.c_str(),
|
||||
strStatusBar.c_str());
|
||||
}
|
||||
|
||||
void CUnsignedAlert::print() const
|
||||
{
|
||||
printf("%s", ToString().c_str());
|
||||
}
|
||||
|
||||
void CAlert::SetNull()
|
||||
{
|
||||
CUnsignedAlert::SetNull();
|
||||
vchMsg.clear();
|
||||
vchSig.clear();
|
||||
}
|
||||
|
||||
bool CAlert::IsNull() const
|
||||
{
|
||||
return (nExpiration == 0);
|
||||
}
|
||||
|
||||
uint256 CAlert::GetHash() const
|
||||
{
|
||||
return Hash(this->vchMsg.begin(), this->vchMsg.end());
|
||||
}
|
||||
|
||||
bool CAlert::IsInEffect() const
|
||||
{
|
||||
return (GetAdjustedTime() < nExpiration);
|
||||
}
|
||||
|
||||
bool CAlert::Cancels(const CAlert& alert) const
|
||||
{
|
||||
if (!IsInEffect())
|
||||
return false; // this was a no-op before 31403
|
||||
return (alert.nID <= nCancel || setCancel.count(alert.nID));
|
||||
}
|
||||
|
||||
bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const
|
||||
{
|
||||
// TODO: rework for client-version-embedded-in-strSubVer ?
|
||||
return (IsInEffect() &&
|
||||
nMinVer <= nVersion && nVersion <= nMaxVer &&
|
||||
(setSubVer.empty() || setSubVer.count(strSubVerIn)));
|
||||
}
|
||||
|
||||
bool CAlert::AppliesToMe() const
|
||||
{
|
||||
return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
|
||||
}
|
||||
|
||||
bool CAlert::RelayTo(CNode* pnode) const
|
||||
{
|
||||
if (!IsInEffect())
|
||||
return false;
|
||||
// returns true if wasn't already contained in the set
|
||||
if (pnode->setKnown.insert(GetHash()).second)
|
||||
{
|
||||
if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
|
||||
AppliesToMe() ||
|
||||
GetAdjustedTime() < nRelayUntil)
|
||||
{
|
||||
pnode->PushMessage("alert", *this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CAlert::CheckSignature() const
|
||||
{
|
||||
// Alert key system disabled for decentralization - v5 hard fork
|
||||
const char* pszKey = fTestNet ? pszTestKey : pszMainKey;
|
||||
if (pszKey[0] == '\0')
|
||||
return false; // No alerts accepted without a valid key
|
||||
|
||||
CKey key;
|
||||
if (!key.SetPubKey(ParseHex(pszKey)))
|
||||
return error("CAlert::CheckSignature() : SetPubKey failed");
|
||||
if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
|
||||
return error("CAlert::CheckSignature() : verify signature failed");
|
||||
|
||||
// Now unserialize the data
|
||||
CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
|
||||
sMsg >> *(CUnsignedAlert*)this;
|
||||
return true;
|
||||
}
|
||||
|
||||
CAlert CAlert::getAlertByHash(const uint256 &hash)
|
||||
{
|
||||
CAlert retval;
|
||||
{
|
||||
LOCK(cs_mapAlerts);
|
||||
map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
|
||||
if(mi != mapAlerts.end())
|
||||
retval = mi->second;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool CAlert::ProcessAlert(bool fThread)
|
||||
{
|
||||
if (!CheckSignature())
|
||||
return false;
|
||||
if (!IsInEffect())
|
||||
return false;
|
||||
|
||||
// alert.nID=max is reserved for if the alert key is
|
||||
// compromised. It must have a pre-defined message,
|
||||
// must never expire, must apply to all versions,
|
||||
// and must cancel all previous
|
||||
// alerts or it will be ignored (so an attacker can't
|
||||
// send an "everything is OK, don't panic" version that
|
||||
// cannot be overridden):
|
||||
int maxInt = std::numeric_limits<int>::max();
|
||||
if (nID == maxInt)
|
||||
{
|
||||
if (!(
|
||||
nExpiration == maxInt &&
|
||||
nCancel == (maxInt-1) &&
|
||||
nMinVer == 0 &&
|
||||
nMaxVer == maxInt &&
|
||||
setSubVer.empty() &&
|
||||
nPriority == maxInt &&
|
||||
strStatusBar == "URGENT: Alert key compromised, upgrade required"
|
||||
))
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
LOCK(cs_mapAlerts);
|
||||
// Cancel previous alerts
|
||||
for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
|
||||
{
|
||||
const CAlert& alert = (*mi).second;
|
||||
if (Cancels(alert))
|
||||
{
|
||||
printf("cancelling alert %d\n", alert.nID);
|
||||
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
|
||||
mapAlerts.erase(mi++);
|
||||
}
|
||||
else if (!alert.IsInEffect())
|
||||
{
|
||||
printf("expiring alert %d\n", alert.nID);
|
||||
uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
|
||||
mapAlerts.erase(mi++);
|
||||
}
|
||||
else
|
||||
mi++;
|
||||
}
|
||||
|
||||
// Check if this alert has been cancelled
|
||||
for (auto& item : mapAlerts)
|
||||
{
|
||||
const CAlert& alert = item.second;
|
||||
if (alert.Cancels(*this))
|
||||
{
|
||||
printf("alert already cancelled by %d\n", alert.nID);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add to mapAlerts
|
||||
mapAlerts.insert(make_pair(GetHash(), *this));
|
||||
// Notify UI and -alertnotify if it applies to me
|
||||
if(AppliesToMe())
|
||||
{
|
||||
uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
|
||||
std::string strCmd = GetArg("-alertnotify", "");
|
||||
if (!strCmd.empty())
|
||||
{
|
||||
// Alert text should be plain ascii coming from a trusted source, but to
|
||||
// be safe we first strip anything not in safeChars, then add single quotes around
|
||||
// the whole string before passing it to the shell:
|
||||
std::string singleQuote("'");
|
||||
// safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything
|
||||
// even possibly remotely dangerous like & or >
|
||||
std::string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@");
|
||||
std::string safeStatus;
|
||||
for (std::string::size_type i = 0; i < strStatusBar.size(); i++)
|
||||
{
|
||||
if (safeChars.find(strStatusBar[i]) != std::string::npos)
|
||||
safeStatus.push_back(strStatusBar[i]);
|
||||
}
|
||||
safeStatus = singleQuote+safeStatus+singleQuote;
|
||||
boost::replace_all(strCmd, "%s", safeStatus);
|
||||
|
||||
if (fThread)
|
||||
boost::thread t(runCommand, strCmd); // thread runs free
|
||||
else
|
||||
runCommand(strCmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
// Copyright (c) 2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef _TRIANGLESALERT_H_
|
||||
#define _TRIANGLESALERT_H_ 1
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
|
||||
class CNode;
|
||||
|
||||
/** Alerts are for notifying old versions if they become too obsolete and
|
||||
* need to upgrade. The message is displayed in the status bar.
|
||||
* Alert messages are broadcast as a vector of signed data. Unserializing may
|
||||
* not read the entire buffer if the alert is for a newer version, but older
|
||||
* versions can still relay the original data.
|
||||
*/
|
||||
class CUnsignedAlert
|
||||
{
|
||||
public:
|
||||
int nVersion;
|
||||
int64_t nRelayUntil; // when newer nodes stop relaying to newer nodes
|
||||
int64_t nExpiration;
|
||||
int nID;
|
||||
int nCancel;
|
||||
std::set<int> setCancel;
|
||||
int nMinVer; // lowest version inclusive
|
||||
int nMaxVer; // highest version inclusive
|
||||
std::set<std::string> setSubVer; // empty matches all
|
||||
int nPriority;
|
||||
|
||||
// Actions
|
||||
std::string strComment;
|
||||
std::string strStatusBar;
|
||||
std::string strReserved;
|
||||
|
||||
IMPLEMENT_SERIALIZE
|
||||
(
|
||||
READWRITE(this->nVersion);
|
||||
nVersion = this->nVersion;
|
||||
READWRITE(nRelayUntil);
|
||||
READWRITE(nExpiration);
|
||||
READWRITE(nID);
|
||||
READWRITE(nCancel);
|
||||
READWRITE(setCancel);
|
||||
READWRITE(nMinVer);
|
||||
READWRITE(nMaxVer);
|
||||
READWRITE(setSubVer);
|
||||
READWRITE(nPriority);
|
||||
|
||||
READWRITE(strComment);
|
||||
READWRITE(strStatusBar);
|
||||
READWRITE(strReserved);
|
||||
)
|
||||
|
||||
void SetNull();
|
||||
|
||||
std::string ToString() const;
|
||||
void print() const;
|
||||
};
|
||||
|
||||
/** An alert is a combination of a serialized CUnsignedAlert and a signature. */
|
||||
class CAlert : public CUnsignedAlert
|
||||
{
|
||||
public:
|
||||
std::vector<unsigned char> vchMsg;
|
||||
std::vector<unsigned char> vchSig;
|
||||
|
||||
CAlert()
|
||||
{
|
||||
SetNull();
|
||||
}
|
||||
|
||||
IMPLEMENT_SERIALIZE
|
||||
(
|
||||
READWRITE(vchMsg);
|
||||
READWRITE(vchSig);
|
||||
)
|
||||
|
||||
void SetNull();
|
||||
bool IsNull() const;
|
||||
uint256 GetHash() const;
|
||||
bool IsInEffect() const;
|
||||
bool Cancels(const CAlert& alert) const;
|
||||
bool AppliesTo(int nVersion, std::string strSubVerIn) const;
|
||||
bool AppliesToMe() const;
|
||||
bool RelayTo(CNode* pnode) const;
|
||||
bool CheckSignature() const;
|
||||
bool ProcessAlert(bool fThread = true);
|
||||
|
||||
/*
|
||||
* Get copy of (active) alert object by hash. Returns a null alert if it is not found.
|
||||
*/
|
||||
static CAlert getAlertByHash(const uint256 &hash);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
// For all pages in affected range, increase lock count
|
||||
void LockRange(void *p, size_t size)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if(!size) return;
|
||||
const size_t base_addr = reinterpret_cast<size_t>(p);
|
||||
const size_t start_page = base_addr & page_mask;
|
||||
@@ -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
|
||||
{
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
// For all pages in affected range, decrease lock count
|
||||
void UnlockRange(void *p, size_t size)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if(!size) return;
|
||||
const size_t base_addr = reinterpret_cast<size_t>(p);
|
||||
const size_t start_page = base_addr & page_mask;
|
||||
@@ -101,13 +101,13 @@ public:
|
||||
// Get number of locked pages for diagnostics
|
||||
int GetLockedPageCount()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return histogram.size();
|
||||
}
|
||||
|
||||
private:
|
||||
Locker locker;
|
||||
boost::mutex mutex;
|
||||
std::mutex mutex;
|
||||
size_t page_size, page_mask;
|
||||
// map of page base address to lock count
|
||||
typedef std::map<size_t,int> Histogram;
|
||||
@@ -182,35 +182,36 @@ private:
|
||||
template<typename T>
|
||||
struct secure_allocator : public std::allocator<T>
|
||||
{
|
||||
// MSVC8 default copy constructor is broken
|
||||
// C++20 removed pointer/reference/etc. member typedefs from std::allocator
|
||||
// and removed the 2-arg allocate(n, hint). Define what we still need
|
||||
// directly instead of pulling from base.
|
||||
typedef std::allocator<T> base;
|
||||
typedef typename base::size_type size_type;
|
||||
typedef typename base::difference_type difference_type;
|
||||
typedef typename base::pointer pointer;
|
||||
typedef typename base::const_pointer const_pointer;
|
||||
typedef typename base::reference reference;
|
||||
typedef typename base::const_reference const_reference;
|
||||
typedef typename base::value_type value_type;
|
||||
secure_allocator() throw() {}
|
||||
secure_allocator(const secure_allocator& a) throw() : base(a) {}
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
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, const void *hint = 0)
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
T *p;
|
||||
p = std::allocator<T>::allocate(n, hint);
|
||||
if (p != NULL)
|
||||
T* p = std::allocator<T>::allocate(n);
|
||||
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);
|
||||
@@ -226,33 +227,34 @@ struct secure_allocator : public std::allocator<T>
|
||||
template<typename T>
|
||||
struct zero_after_free_allocator : public std::allocator<T>
|
||||
{
|
||||
// MSVC8 default copy constructor is broken
|
||||
// C++20 removed pointer/reference/etc. member typedefs from std::allocator.
|
||||
// Define what we still need directly instead of pulling from base.
|
||||
typedef std::allocator<T> base;
|
||||
typedef typename base::size_type size_type;
|
||||
typedef typename base::difference_type difference_type;
|
||||
typedef typename base::pointer pointer;
|
||||
typedef typename base::const_pointer const_pointer;
|
||||
typedef typename base::reference reference;
|
||||
typedef typename base::const_reference const_reference;
|
||||
typedef typename base::value_type value_type;
|
||||
zero_after_free_allocator() throw() {}
|
||||
zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {}
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
#include "version.h"
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
@@ -36,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); }
|
||||
};
|
||||
|
||||
|
||||
@@ -63,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))
|
||||
{
|
||||
@@ -88,7 +90,7 @@ public:
|
||||
|
||||
~CBigNum()
|
||||
{
|
||||
if (pbn != NULL)
|
||||
if (pbn != nullptr)
|
||||
BN_clear_free(pbn);
|
||||
}
|
||||
|
||||
@@ -219,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);
|
||||
@@ -289,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);
|
||||
@@ -320,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);
|
||||
@@ -344,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]);
|
||||
@@ -373,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))
|
||||
{
|
||||
@@ -514,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;
|
||||
}
|
||||
@@ -540,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");
|
||||
}
|
||||
@@ -705,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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
#include "bootstrap.h"
|
||||
#include "utxosnapshot.h"
|
||||
#include "txdb.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
extern bool fTestNet;
|
||||
namespace Checkpoints { bool IsKnownCheckpoint(int nHeight, const uint256& hash); }
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Bootstrap {
|
||||
|
||||
@@ -63,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;
|
||||
@@ -299,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 ||
|
||||
@@ -415,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);
|
||||
}
|
||||
@@ -575,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;
|
||||
|
||||
@@ -585,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());
|
||||
@@ -718,16 +718,16 @@ bool DownloadBootstrap(const std::string& host,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the archive included a trusted pre-built index (txleveldb/)
|
||||
// with a valid snapshot.manifest. If verified, keep it to skip the
|
||||
// Check if the archive included a trusted pre-built index for the active
|
||||
// backend with a valid snapshot.manifest. If verified, keep it to skip the
|
||||
// multi-hour FastImportBlockFile() rebuild.
|
||||
fs::path txleveldb = dataDir / "txleveldb";
|
||||
fs::path chainDbPath = GetChainDataDir();
|
||||
fs::path database = dataDir / "database";
|
||||
fs::path manifestPath = dataDir / "snapshot.manifest";
|
||||
|
||||
bool keepIndex = false;
|
||||
|
||||
if (fs::exists(manifestPath) && fs::exists(txleveldb)) {
|
||||
if (fs::exists(manifestPath) && fs::exists(chainDbPath)) {
|
||||
SnapshotManifest manifest;
|
||||
std::string manifestError;
|
||||
|
||||
@@ -754,9 +754,10 @@ bool DownloadBootstrap(const std::string& host,
|
||||
if (!keepIndex) {
|
||||
// No valid manifest or verification failed - delete the index.
|
||||
// FastImportBlockFile() will rebuild from blk0001.dat on next startup.
|
||||
printf("Bootstrap: removing extracted txleveldb/ (will rebuild index from blk0001.dat)\n");
|
||||
if (fs::exists(txleveldb))
|
||||
fs::remove_all(txleveldb);
|
||||
printf("Bootstrap: removing extracted %s/ (will rebuild index from blk0001.dat)\n",
|
||||
GetChainDataDir().filename().string().c_str());
|
||||
if (fs::exists(chainDbPath))
|
||||
fs::remove_all(chainDbPath);
|
||||
}
|
||||
|
||||
// Always remove BDB database/ dir (wallet environment from another machine)
|
||||
@@ -791,7 +792,7 @@ bool DownloadUtxoSnapshot(const std::string& host,
|
||||
|
||||
printf("Bootstrap: UTXO snapshot downloaded, loading into database...\n");
|
||||
|
||||
// Load the snapshot into a fresh txleveldb
|
||||
// Load the snapshot into a fresh active chain DB
|
||||
if (!UtxoSnapshot::LoadSnapshot(tmpPath, dataDir, strError)) {
|
||||
fs::remove(tmpPath);
|
||||
return false;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
namespace Bootstrap {
|
||||
|
||||
@@ -20,14 +20,14 @@ namespace Bootstrap {
|
||||
typedef std::function<void(int64_t, int64_t)> ProgressCallback;
|
||||
|
||||
// Check if data dir already has blockchain data
|
||||
bool NeedsBootstrap(const boost::filesystem::path& dataDir);
|
||||
bool NeedsBootstrap(const std::filesystem::path& dataDir);
|
||||
|
||||
// Download a single file via HTTP GET, write to destPath.
|
||||
// If noProxy is true, bypass Tor SOCKS proxy and connect directly
|
||||
// (used for clearnet bootstrap downloads).
|
||||
// If portOverride is set (>0), uses that port instead of the default PORT.
|
||||
bool DownloadFile(const std::string& host, const std::string& urlPath,
|
||||
const boost::filesystem::path& destPath,
|
||||
const std::filesystem::path& destPath,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError,
|
||||
bool noProxy = false,
|
||||
@@ -42,7 +42,7 @@ namespace Bootstrap {
|
||||
// Download bootstrap.tar.gz and extract to dataDir.
|
||||
// Falls back to filelist.txt + individual file download if tar.gz unavailable.
|
||||
bool DownloadBootstrap(const std::string& host,
|
||||
const boost::filesystem::path& dataDir,
|
||||
const std::filesystem::path& dataDir,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError);
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Bootstrap {
|
||||
};
|
||||
|
||||
// Parse a snapshot.manifest file into a SnapshotManifest struct.
|
||||
bool ParseManifest(const boost::filesystem::path& manifestPath,
|
||||
bool ParseManifest(const std::filesystem::path& manifestPath,
|
||||
SnapshotManifest& manifest,
|
||||
std::string& strError);
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Bootstrap {
|
||||
// This is much faster than downloading the full bootstrap archive.
|
||||
// Returns true if snapshot was downloaded and loaded successfully.
|
||||
bool DownloadUtxoSnapshot(const std::string& host,
|
||||
const boost::filesystem::path& dataDir,
|
||||
const std::filesystem::path& dataDir,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -32,13 +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")},
|
||||
{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.
|
||||
@@ -50,7 +48,7 @@ namespace Checkpoints
|
||||
// here. The corresponding (height, blockhash) must already exist in
|
||||
// mapCheckpoints / mapCheckpointsTestnet.
|
||||
static std::map<int, uint256> mapSnapshotHashes = {
|
||||
// {2186940, uint256("0x...sha256-of-utxo-snapshot.bin...")},
|
||||
{ 2206004, uint256("0x1419282dae817315ee1b955543f6248233fe5800f5e8488734a0ece5bd6781ea")},
|
||||
};
|
||||
|
||||
static std::map<int, uint256> mapSnapshotHashesTestnet = {
|
||||
@@ -69,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)
|
||||
@@ -131,7 +120,7 @@ namespace Checkpoints
|
||||
if (t != mapBlockIndex.end())
|
||||
return t->second;
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// triangles: synchronized checkpoint (centrally broadcasted)
|
||||
@@ -150,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
|
||||
@@ -198,7 +187,7 @@ namespace Checkpoints
|
||||
|
||||
bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
|
||||
{
|
||||
CTxDB txdb;
|
||||
auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder;
|
||||
txdb.TxnBegin();
|
||||
if (!txdb.WriteSyncCheckpoint(hashCheckpoint))
|
||||
{
|
||||
@@ -224,7 +213,7 @@ namespace Checkpoints
|
||||
return false;
|
||||
}
|
||||
|
||||
CTxDB txdb;
|
||||
auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder;
|
||||
CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint];
|
||||
if (!pindexCheckpoint->IsInMainChain())
|
||||
{
|
||||
@@ -280,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;
|
||||
}
|
||||
@@ -295,7 +284,7 @@ namespace Checkpoints
|
||||
{
|
||||
// checkpoint block accepted but not yet in main chain
|
||||
printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str());
|
||||
CTxDB txdb;
|
||||
auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder;
|
||||
CBlock block;
|
||||
if (!block.ReadFromDisk(mapBlockIndex[hash]))
|
||||
return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str());
|
||||
@@ -370,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;
|
||||
@@ -431,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;
|
||||
}
|
||||
@@ -439,7 +428,7 @@ bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
|
||||
if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint))
|
||||
return false;
|
||||
|
||||
CTxDB txdb;
|
||||
auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder;
|
||||
CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint];
|
||||
if (!pindexCheckpoint->IsInMainChain())
|
||||
{
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread/condition_variable.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
template<typename T>
|
||||
class CCheckQueue
|
||||
{
|
||||
private:
|
||||
boost::mutex mutex;
|
||||
boost::condition_variable condWorker;
|
||||
boost::condition_variable condMaster;
|
||||
std::mutex mutex;
|
||||
std::condition_variable condWorker;
|
||||
std::condition_variable condMaster;
|
||||
|
||||
std::deque<T> queue;
|
||||
unsigned int nIdle;
|
||||
@@ -32,7 +32,7 @@ private:
|
||||
|
||||
bool Loop(bool fMaster)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (!fMaster)
|
||||
nTotal++;
|
||||
nIdle++;
|
||||
@@ -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();
|
||||
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
|
||||
void StartBatch()
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
fAllOk = true;
|
||||
nTodo = 0;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
if (vChecks.empty())
|
||||
return;
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
for (typename std::vector<T>::iterator it = vChecks.begin(); it != vChecks.end(); ++it)
|
||||
{
|
||||
queue.push_back(T());
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
|
||||
void Quit()
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
fQuit = true;
|
||||
condWorker.notify_all();
|
||||
condMaster.notify_all();
|
||||
|
||||
@@ -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 5
|
||||
#define CLIENT_VERSION_REVISION 15
|
||||
#define CLIENT_VERSION_BUILD 0
|
||||
|
||||
// Converts the parameter X to a string after macro replacement on X has been performed.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "key.h"
|
||||
#include "serialize.h"
|
||||
|
||||
#include <openssl/crypto.h> /* for OPENSSL_cleanse */
|
||||
|
||||
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
|
||||
const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
|
||||
|
||||
@@ -78,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
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 "crypto_ecdh.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_ecdh.h>
|
||||
|
||||
namespace {
|
||||
|
||||
// One process-wide context is sufficient for ECDH — no signing or verification
|
||||
// flags needed. Created lazily on first use; libsecp256k1 contexts are
|
||||
// thread-safe for read-only operations like ECDH.
|
||||
secp256k1_context* GetECDHContext()
|
||||
{
|
||||
static std::once_flag once;
|
||||
static secp256k1_context* ctx = nullptr;
|
||||
std::call_once(once, []() {
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
});
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// Hash function callback that returns the raw X coordinate of the shared
|
||||
// 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*/,
|
||||
void* /*data*/)
|
||||
{
|
||||
std::memcpy(output, x32, 32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ECDH_xonly_secp256k1(unsigned char out32[32],
|
||||
const unsigned char privkey32[32],
|
||||
const unsigned char* pubkey,
|
||||
std::size_t pubkey_len)
|
||||
{
|
||||
if (pubkey_len != 33 && pubkey_len != 65) return false;
|
||||
|
||||
secp256k1_context* ctx = GetECDHContext();
|
||||
if (!ctx) return false;
|
||||
|
||||
secp256k1_pubkey pk;
|
||||
if (!secp256k1_ec_pubkey_parse(ctx, &pk, pubkey, pubkey_len))
|
||||
return false;
|
||||
|
||||
return secp256k1_ecdh(ctx, out32, &pk, privkey32, hash_xonly, nullptr) == 1;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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_CRYPTO_ECDH_H
|
||||
#define TRIANGLES_CRYPTO_ECDH_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
/**
|
||||
* Compute the shared secret X coordinate via secp256k1 ECDH.
|
||||
*
|
||||
* Output matches OpenSSL's ECDH_compute_key(buf, 32, peer_pub, our_priv, NULL)
|
||||
* — i.e. the raw X coordinate of the shared point, with no KDF applied. This
|
||||
* preserves bit-for-bit compatibility with smessage's existing key derivation
|
||||
* (which feeds the X coordinate into SHA-512 itself), so historical encrypted
|
||||
* messages remain decryptable after the migration off OpenSSL EC.
|
||||
*
|
||||
* @param out32 32-byte buffer for the shared X coordinate.
|
||||
* @param privkey32 32-byte secret scalar (big-endian).
|
||||
* @param pubkey Peer public key, serialized as either 33 bytes (compressed)
|
||||
* or 65 bytes (uncompressed).
|
||||
* @param pubkey_len 33 or 65; any other length fails immediately.
|
||||
* @return true on success, false if the public key is malformed or the
|
||||
* private key is invalid (zero / >= curve order).
|
||||
*/
|
||||
bool ECDH_xonly_secp256k1(unsigned char out32[32],
|
||||
const unsigned char privkey32[32],
|
||||
const unsigned char* pubkey,
|
||||
std::size_t pubkey_len);
|
||||
|
||||
#endif // TRIANGLES_CRYPTO_ECDH_H
|
||||
@@ -0,0 +1,389 @@
|
||||
// Copyright (c) 2026 The Triangles developers
|
||||
// Copyright (c) 2015 Pieter Wuille (lax DER parser, MIT licence)
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "crypto_ecdsa.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_recovery.h>
|
||||
|
||||
namespace {
|
||||
|
||||
// Combined VERIFY + SIGN context. libsecp256k1 contexts are thread-safe for
|
||||
// signing and verification once created. In libsecp256k1 >= 0.2 these flags
|
||||
// are accepted but increasingly no-ops; passing both keeps us compatible with
|
||||
// older versions still in distro packages.
|
||||
secp256k1_context* GetEcdsaContext()
|
||||
{
|
||||
static std::once_flag once;
|
||||
static secp256k1_context* ctx = nullptr;
|
||||
std::call_once(once, []() {
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
});
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Lax DER parser, vendored from Bitcoin Core (contrib/lax_der_parsing.c).
|
||||
//
|
||||
// libsecp256k1's strict parser rejects DER encodings that OpenSSL has
|
||||
// historically accepted: non-minimal length bytes, extra leading zeros on R/S,
|
||||
// negative integers, etc. Many such signatures already exist on chain. This
|
||||
// parser tolerates them, normalises (R, S) into a 64-byte compact buffer, and
|
||||
// hands that to libsecp256k1's compact-signature parser. Anything that still
|
||||
// fails to fit (e.g. R or S exceeding 32 bytes after stripping leading zeros)
|
||||
// is treated as zero so the verify call returns a clean failure rather than
|
||||
// crashing.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx,
|
||||
secp256k1_ecdsa_signature* sig,
|
||||
const unsigned char* input,
|
||||
std::size_t inputlen)
|
||||
{
|
||||
std::size_t rpos, rlen, spos, slen;
|
||||
std::size_t pos = 0;
|
||||
std::size_t lenbyte;
|
||||
unsigned char tmpsig[64] = {0};
|
||||
int overflow = 0;
|
||||
|
||||
// Initialise sig with a parseable but invalid signature so the caller
|
||||
// always gets a defined value back even on early-exit paths.
|
||||
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
|
||||
|
||||
// SEQUENCE tag.
|
||||
if (pos == inputlen || input[pos] != 0x30) return 0;
|
||||
pos++;
|
||||
|
||||
// SEQUENCE length (skipped — we trust the inner element lengths).
|
||||
if (pos == inputlen) return 0;
|
||||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (lenbyte > inputlen - pos) return 0;
|
||||
pos += lenbyte;
|
||||
}
|
||||
|
||||
// R: INTEGER tag.
|
||||
if (pos == inputlen || input[pos] != 0x02) return 0;
|
||||
pos++;
|
||||
|
||||
// R: length.
|
||||
if (pos == inputlen) return 0;
|
||||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (lenbyte > inputlen - pos) return 0;
|
||||
while (lenbyte > 0 && input[pos] == 0) { pos++; lenbyte--; }
|
||||
if (lenbyte >= sizeof(std::size_t)) return 0;
|
||||
rlen = 0;
|
||||
while (lenbyte > 0) { rlen = (rlen << 8) + input[pos]; pos++; lenbyte--; }
|
||||
} else {
|
||||
rlen = lenbyte;
|
||||
}
|
||||
if (rlen > inputlen - pos) return 0;
|
||||
rpos = pos;
|
||||
pos += rlen;
|
||||
|
||||
// S: INTEGER tag.
|
||||
if (pos == inputlen || input[pos] != 0x02) return 0;
|
||||
pos++;
|
||||
|
||||
// S: length.
|
||||
if (pos == inputlen) return 0;
|
||||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (lenbyte > inputlen - pos) return 0;
|
||||
while (lenbyte > 0 && input[pos] == 0) { pos++; lenbyte--; }
|
||||
if (lenbyte >= sizeof(std::size_t)) return 0;
|
||||
slen = 0;
|
||||
while (lenbyte > 0) { slen = (slen << 8) + input[pos]; pos++; lenbyte--; }
|
||||
} else {
|
||||
slen = lenbyte;
|
||||
}
|
||||
if (slen > inputlen - pos) return 0;
|
||||
spos = pos;
|
||||
|
||||
// Strip leading zeros from R and place right-aligned in tmpsig[0..32).
|
||||
while (rlen > 0 && input[rpos] == 0) { rlen--; rpos++; }
|
||||
if (rlen > 32) {
|
||||
overflow = 1;
|
||||
} else {
|
||||
std::memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
|
||||
}
|
||||
|
||||
// Strip leading zeros from S and place right-aligned in tmpsig[32..64).
|
||||
while (slen > 0 && input[spos] == 0) { slen--; spos++; }
|
||||
if (slen > 32) {
|
||||
overflow = 1;
|
||||
} else {
|
||||
std::memcpy(tmpsig + 64 - slen, input + spos, slen);
|
||||
}
|
||||
|
||||
if (!overflow) {
|
||||
overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
|
||||
}
|
||||
if (overflow) {
|
||||
std::memset(tmpsig, 0, 64);
|
||||
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ECDSA_verify_secp256k1(const unsigned char hash32[32],
|
||||
const unsigned char* sig, std::size_t sig_len,
|
||||
const unsigned char* pubkey, std::size_t pubkey_len)
|
||||
{
|
||||
if (sig_len == 0) return false;
|
||||
if (pubkey_len != 33 && pubkey_len != 65) return false;
|
||||
|
||||
secp256k1_context* ctx = GetEcdsaContext();
|
||||
if (!ctx) return false;
|
||||
|
||||
secp256k1_pubkey pk;
|
||||
if (!secp256k1_ec_pubkey_parse(ctx, &pk, pubkey, pubkey_len))
|
||||
return false;
|
||||
|
||||
secp256k1_ecdsa_signature parsed_sig;
|
||||
if (!ecdsa_signature_parse_der_lax(ctx, &parsed_sig, sig, sig_len))
|
||||
return false;
|
||||
|
||||
return secp256k1_ecdsa_verify(ctx, &parsed_sig, hash32, &pk) == 1;
|
||||
}
|
||||
|
||||
bool ECDSA_sign_secp256k1(unsigned char* out, std::size_t* out_len,
|
||||
const unsigned char hash32[32],
|
||||
const unsigned char privkey32[32])
|
||||
{
|
||||
if (!out || !out_len) return false;
|
||||
secp256k1_context* ctx = GetEcdsaContext();
|
||||
if (!ctx) return false;
|
||||
|
||||
secp256k1_ecdsa_signature sig;
|
||||
if (!secp256k1_ecdsa_sign(ctx, &sig, hash32, privkey32, nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
return secp256k1_ecdsa_signature_serialize_der(ctx, out, out_len, &sig) == 1;
|
||||
}
|
||||
|
||||
bool ECDSA_sign_compact_secp256k1(unsigned char out65[65],
|
||||
const unsigned char hash32[32],
|
||||
const unsigned char privkey32[32],
|
||||
bool fCompressed)
|
||||
{
|
||||
secp256k1_context* ctx = GetEcdsaContext();
|
||||
if (!ctx) return false;
|
||||
|
||||
secp256k1_ecdsa_recoverable_signature recsig;
|
||||
if (!secp256k1_ecdsa_sign_recoverable(ctx, &recsig, hash32, privkey32, nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
int recid = -1;
|
||||
if (!secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, &out65[1], &recid, &recsig))
|
||||
return false;
|
||||
if (recid < 0 || recid > 3) return false;
|
||||
|
||||
out65[0] = static_cast<unsigned char>(27 + recid + (fCompressed ? 4 : 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECDSA_recover_compact_secp256k1(unsigned char* pubkey_out,
|
||||
std::size_t* pubkey_len_out,
|
||||
const unsigned char hash32[32],
|
||||
const unsigned char sig65[65])
|
||||
{
|
||||
if (!pubkey_out || !pubkey_len_out) return false;
|
||||
|
||||
int header = sig65[0];
|
||||
if (header < 27 || header >= 35) return false;
|
||||
bool fCompressed = (header >= 31);
|
||||
int recid = (header - 27) & 0x3;
|
||||
|
||||
secp256k1_context* ctx = GetEcdsaContext();
|
||||
if (!ctx) return false;
|
||||
|
||||
secp256k1_ecdsa_recoverable_signature recsig;
|
||||
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &recsig, &sig65[1], recid))
|
||||
return false;
|
||||
|
||||
secp256k1_pubkey pk;
|
||||
if (!secp256k1_ecdsa_recover(ctx, &pk, &recsig, hash32))
|
||||
return false;
|
||||
|
||||
std::size_t out_len = fCompressed ? 33 : 65;
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &out_len, &pk,
|
||||
fCompressed ? SECP256K1_EC_COMPRESSED
|
||||
: SECP256K1_EC_UNCOMPRESSED))
|
||||
return false;
|
||||
|
||||
*pubkey_len_out = out_len;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECDSA_seckey_verify_secp256k1(const unsigned char privkey32[32])
|
||||
{
|
||||
secp256k1_context* ctx = GetEcdsaContext();
|
||||
if (!ctx) return false;
|
||||
return secp256k1_ec_seckey_verify(ctx, privkey32) == 1;
|
||||
}
|
||||
|
||||
bool ECDSA_pubkey_verify_secp256k1(const unsigned char* pubkey, std::size_t pubkey_len)
|
||||
{
|
||||
if (pubkey_len != 33 && pubkey_len != 65) return false;
|
||||
secp256k1_context* ctx = GetEcdsaContext();
|
||||
if (!ctx) return false;
|
||||
secp256k1_pubkey pk;
|
||||
return secp256k1_ec_pubkey_parse(ctx, &pk, pubkey, pubkey_len) == 1;
|
||||
}
|
||||
|
||||
bool ECDSA_pubkey_from_privkey_secp256k1(unsigned char* out, std::size_t* out_len_out,
|
||||
const unsigned char privkey32[32],
|
||||
bool fCompressed)
|
||||
{
|
||||
if (!out || !out_len_out) return false;
|
||||
secp256k1_context* ctx = GetEcdsaContext();
|
||||
if (!ctx) return false;
|
||||
|
||||
secp256k1_pubkey pk;
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &pk, privkey32))
|
||||
return false;
|
||||
|
||||
std::size_t len = fCompressed ? 33 : 65;
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, out, &len, &pk,
|
||||
fCompressed ? SECP256K1_EC_COMPRESSED
|
||||
: SECP256K1_EC_UNCOMPRESSED))
|
||||
return false;
|
||||
*out_len_out = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// SEC1 / RFC-5915 DER codec for secp256k1 ECPrivateKey
|
||||
//
|
||||
// Vendored from Bitcoin Core (src/key.cpp), MIT-licensed. The decoder is lax
|
||||
// about details (matches OpenSSL's d2i_ECPrivateKey lenience); the encoder
|
||||
// writes the exact byte layout that OpenSSL's i2d_ECPrivateKey produces for
|
||||
// this curve so wallet.dat records remain interchangeable across versions.
|
||||
//
|
||||
// Compressed pubkey: 214 bytes
|
||||
// Uncompressed pubkey: 279 bytes
|
||||
//
|
||||
// The static templates below carry every byte except the 32-byte private
|
||||
// scalar and the public key bytes, which are spliced into the precomputed
|
||||
// offsets at encode time.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
namespace {
|
||||
|
||||
const unsigned char der_template_compressed[214] = {
|
||||
0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20,
|
||||
/* private key (32 bytes) at offset 8 */
|
||||
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,
|
||||
0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
|
||||
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,
|
||||
0x04,0x01,0x07,0x04,0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,
|
||||
0x62,0x95,0xCE,0x87,0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,
|
||||
0x81,0x5B,0x16,0xF8,0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,
|
||||
0x3B,0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,
|
||||
0x00,
|
||||
/* compressed pubkey (33 bytes) at offset 181 */
|
||||
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
|
||||
};
|
||||
|
||||
const unsigned char der_template_uncompressed[279] = {
|
||||
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20,
|
||||
/* private key (32 bytes) at offset 9 */
|
||||
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,
|
||||
0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
|
||||
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,
|
||||
0x04,0x01,0x07,0x04,0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,
|
||||
0x62,0x95,0xCE,0x87,0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,
|
||||
0x81,0x5B,0x16,0xF8,0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,
|
||||
0xFB,0xFC,0x0E,0x11,0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,
|
||||
0xD0,0x8F,0xFB,0x10,0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,
|
||||
0x3B,0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,
|
||||
0x00,
|
||||
/* uncompressed pubkey (65 bytes) at offset 214 */
|
||||
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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ECDSA_privkey_export_der_secp256k1(unsigned char* out, std::size_t* out_len_out,
|
||||
const unsigned char privkey32[32],
|
||||
bool fCompressed)
|
||||
{
|
||||
if (!out || !out_len_out) return false;
|
||||
|
||||
secp256k1_context* ctx = GetEcdsaContext();
|
||||
if (!ctx) return false;
|
||||
|
||||
secp256k1_pubkey pk;
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &pk, privkey32))
|
||||
return false;
|
||||
|
||||
if (fCompressed) {
|
||||
std::memcpy(out, der_template_compressed, sizeof(der_template_compressed));
|
||||
std::memcpy(out + 8, privkey32, 32);
|
||||
std::size_t pub_len = 33;
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, out + 181, &pub_len, &pk, SECP256K1_EC_COMPRESSED))
|
||||
return false;
|
||||
*out_len_out = sizeof(der_template_compressed);
|
||||
} else {
|
||||
std::memcpy(out, der_template_uncompressed, sizeof(der_template_uncompressed));
|
||||
std::memcpy(out + 9, privkey32, 32);
|
||||
std::size_t pub_len = 65;
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, out + 214, &pub_len, &pk, SECP256K1_EC_UNCOMPRESSED))
|
||||
return false;
|
||||
*out_len_out = sizeof(der_template_uncompressed);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECDSA_privkey_import_der_secp256k1(unsigned char privkey32_out[32],
|
||||
const unsigned char* der, std::size_t der_len)
|
||||
{
|
||||
// Lax SEC1/RFC-5915 ECPrivateKey parser. We only need to find the OCTET
|
||||
// STRING containing the private key scalar; everything else (curve params,
|
||||
// optional public key) is informational. Mirrors Bitcoin Core's
|
||||
// ec_privkey_import_der.
|
||||
const unsigned char* end = der + der_len;
|
||||
if (end < der + 1 || *(der++) != 0x30) return false;
|
||||
|
||||
// Outer SEQUENCE length — variable length encoding.
|
||||
if (der >= end) return false;
|
||||
int lenb = *(der++);
|
||||
if (lenb < 0x80) {
|
||||
// short form, ignore
|
||||
} else {
|
||||
int n = lenb & 0x7F;
|
||||
if (n == 0 || n > 2) return false;
|
||||
if (der + n > end) return false;
|
||||
der += n;
|
||||
}
|
||||
|
||||
// Version INTEGER (1).
|
||||
if (der + 3 > end || der[0] != 0x02 || der[1] != 0x01 || der[2] != 0x01) return false;
|
||||
der += 3;
|
||||
|
||||
// privateKey OCTET STRING (length 32).
|
||||
if (der + 2 > end || der[0] != 0x04 || der[1] != 0x20) return false;
|
||||
der += 2;
|
||||
if (der + 32 > end) return false;
|
||||
std::memcpy(privkey32_out, der, 32);
|
||||
|
||||
// Validate the result against the curve order; reject zero / >= n.
|
||||
return ECDSA_seckey_verify_secp256k1(privkey32_out);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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_CRYPTO_ECDSA_H
|
||||
#define TRIANGLES_CRYPTO_ECDSA_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
/**
|
||||
* Verify a DER-encoded secp256k1 ECDSA signature using libsecp256k1.
|
||||
*
|
||||
* Drop-in replacement for OpenSSL's
|
||||
* ECDSA_verify(0, hash, 32, sig, sig_len, pkey)
|
||||
* with one important caveat baked in: the DER input is parsed *laxly*
|
||||
* (Bitcoin Core's `lax_der_parsing` algorithm), so historical non-canonical
|
||||
* encodings already on chain — extra padding, leading zeros, length-byte
|
||||
* quirks that OpenSSL's permissive ASN.1 reader once accepted — continue
|
||||
* to verify. Strict-DER-only parsing here would silently fork the chain.
|
||||
*
|
||||
* High-S signatures are accepted (libsecp256k1's verify behaviour by default).
|
||||
* No malleability check is applied; that is policy and lives elsewhere.
|
||||
*
|
||||
* @param hash32 32-byte message hash to verify against.
|
||||
* @param sig DER-encoded signature bytes.
|
||||
* @param sig_len Length of `sig`.
|
||||
* @param pubkey Serialized public key (33 bytes compressed or 65 uncompressed).
|
||||
* @param pubkey_len 33 or 65; any other length fails immediately.
|
||||
* @return true iff the signature is valid for (hash32, pubkey).
|
||||
*/
|
||||
bool ECDSA_verify_secp256k1(const unsigned char hash32[32],
|
||||
const unsigned char* sig, std::size_t sig_len,
|
||||
const unsigned char* pubkey, std::size_t pubkey_len);
|
||||
|
||||
/**
|
||||
* Sign `hash32` with `privkey32` and write a DER-encoded signature to `out`.
|
||||
*
|
||||
* libsecp256k1 uses RFC 6979 deterministic nonces, so signature bytes will
|
||||
* differ from OpenSSL's random-nonce output for the same key+hash, but any
|
||||
* resulting signature is equally valid. Low-S is enforced automatically.
|
||||
*
|
||||
* @param out Output buffer; must be at least `*out_len` bytes.
|
||||
* libsecp256k1 produces at most 72 bytes of DER.
|
||||
* @param out_len In: capacity of `out`. Out: bytes actually written.
|
||||
* @param hash32 32-byte message hash to sign.
|
||||
* @param privkey32 32-byte secret scalar.
|
||||
* @return true on success.
|
||||
*/
|
||||
bool ECDSA_sign_secp256k1(unsigned char* out, std::size_t* out_len,
|
||||
const unsigned char hash32[32],
|
||||
const unsigned char privkey32[32]);
|
||||
|
||||
/**
|
||||
* Produce a 65-byte recoverable compact signature.
|
||||
*
|
||||
* Output layout matches the existing wire format:
|
||||
* out[0] = 27 + recid + (fCompressed ? 4 : 0)
|
||||
* out[1..33) = R (big-endian, 32 bytes)
|
||||
* out[33..65) = S (big-endian, 32 bytes)
|
||||
*
|
||||
* @param out65 65-byte output buffer.
|
||||
* @param hash32 32-byte message hash to sign.
|
||||
* @param privkey32 32-byte secret scalar.
|
||||
* @param fCompressed Whether the matching public key is compressed; affects
|
||||
* the recid offset in the header byte.
|
||||
* @return true on success.
|
||||
*/
|
||||
bool ECDSA_sign_compact_secp256k1(unsigned char out65[65],
|
||||
const unsigned char hash32[32],
|
||||
const unsigned char privkey32[32],
|
||||
bool fCompressed);
|
||||
|
||||
/**
|
||||
* Recover the signing public key from a 65-byte compact signature (as produced
|
||||
* by ECDSA_sign_compact_secp256k1) and a message hash.
|
||||
*
|
||||
* The header byte's "compressed" flag determines whether the recovered key is
|
||||
* serialized as 33 bytes (compressed) or 65 bytes (uncompressed).
|
||||
*
|
||||
* @param pubkey_out Output buffer; needs at least 65 bytes capacity.
|
||||
* @param pubkey_len_out Receives the actual serialized length (33 or 65).
|
||||
* @param hash32 32-byte message hash that was signed.
|
||||
* @param sig65 65-byte compact signature.
|
||||
* @return true if recovery succeeded.
|
||||
*/
|
||||
bool ECDSA_recover_compact_secp256k1(unsigned char* pubkey_out,
|
||||
std::size_t* pubkey_len_out,
|
||||
const unsigned char hash32[32],
|
||||
const unsigned char sig65[65]);
|
||||
|
||||
/** Return true iff `privkey32` is a valid secp256k1 secret (in (0, n)). */
|
||||
bool ECDSA_seckey_verify_secp256k1(const unsigned char privkey32[32]);
|
||||
|
||||
/** Return true iff `pubkey/pubkey_len` parses as a valid secp256k1 point. */
|
||||
bool ECDSA_pubkey_verify_secp256k1(const unsigned char* pubkey, std::size_t pubkey_len);
|
||||
|
||||
/**
|
||||
* Derive the public key for `privkey32` and serialize it.
|
||||
* @param out Output buffer; must be at least 65 bytes.
|
||||
* @param out_len_out Receives the actual length (33 or 65).
|
||||
* @param privkey32 32-byte secret scalar.
|
||||
* @param fCompressed Whether to serialize compressed (33B) or uncompressed (65B).
|
||||
* @return true on success.
|
||||
*/
|
||||
bool ECDSA_pubkey_from_privkey_secp256k1(unsigned char* out, std::size_t* out_len_out,
|
||||
const unsigned char privkey32[32],
|
||||
bool fCompressed);
|
||||
|
||||
/**
|
||||
* SEC1/RFC-5915 DER ECPrivateKey encoder/decoder for the secp256k1 curve.
|
||||
* Output bytes match the layout produced by OpenSSL's i2d_ECPrivateKey on this
|
||||
* curve (compressed = 214 bytes, uncompressed = 279 bytes), so wallet.dat
|
||||
* records written by previous OpenSSL-EC builds remain readable, and records
|
||||
* we write remain readable by older OpenSSL-based builds.
|
||||
*/
|
||||
bool ECDSA_privkey_export_der_secp256k1(unsigned char* out, std::size_t* out_len_out,
|
||||
const unsigned char privkey32[32],
|
||||
bool fCompressed);
|
||||
bool ECDSA_privkey_import_der_secp256k1(unsigned char privkey32_out[32],
|
||||
const unsigned char* der, std::size_t der_len);
|
||||
|
||||
#endif // TRIANGLES_CRYPTO_ECDSA_H
|
||||
@@ -8,16 +8,15 @@
|
||||
#include "util.h"
|
||||
#include "main.h"
|
||||
#include "ui_interface.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#ifndef WIN32
|
||||
#include "sys/stat.h"
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
unsigned int nWalletDBUpdated;
|
||||
@@ -134,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 |
|
||||
@@ -156,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:
|
||||
@@ -179,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");
|
||||
@@ -232,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'));
|
||||
@@ -252,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);
|
||||
|
||||
@@ -265,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
|
||||
@@ -275,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));
|
||||
@@ -308,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;
|
||||
@@ -332,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,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);
|
||||
}
|
||||
|
||||
@@ -372,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
|
||||
@@ -413,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;
|
||||
}
|
||||
@@ -429,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)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -28,7 +29,7 @@ extern unsigned int nWalletDBUpdated;
|
||||
|
||||
void ThreadFlushWalletDB(void* parg);
|
||||
bool BackupWallet(const CWallet& wallet, const std::string& strDest);
|
||||
bool AutoBackupWallet(const boost::filesystem::path& walletPath);
|
||||
bool AutoBackupWallet(const std::filesystem::path& walletPath);
|
||||
|
||||
|
||||
class CDBEnv
|
||||
@@ -37,7 +38,7 @@ private:
|
||||
bool fDetachDB;
|
||||
bool fDbEnvInit;
|
||||
bool fMockDb;
|
||||
boost::filesystem::path pathEnv;
|
||||
std::filesystem::path pathEnv;
|
||||
std::string strPath;
|
||||
|
||||
void EnvShutdown();
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
|
||||
bool Salvage(std::string strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
|
||||
|
||||
bool Open(boost::filesystem::path pathEnv_);
|
||||
bool Open(std::filesystem::path pathEnv_);
|
||||
void Close();
|
||||
void Flush(bool fShutdown);
|
||||
void CheckpointLSN(std::string strFile);
|
||||
@@ -83,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;
|
||||
}
|
||||
};
|
||||
@@ -129,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
|
||||
@@ -221,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;
|
||||
}
|
||||
|
||||
@@ -249,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
|
||||
@@ -285,7 +286,7 @@ public:
|
||||
if (!pdb || !activeTxn)
|
||||
return false;
|
||||
int ret = activeTxn->commit(0);
|
||||
activeTxn = NULL;
|
||||
activeTxn = nullptr;
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
@@ -294,7 +295,7 @@ public:
|
||||
if (!pdb || !activeTxn)
|
||||
return false;
|
||||
int ret = activeTxn->abort();
|
||||
activeTxn = NULL;
|
||||
activeTxn = nullptr;
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
@@ -309,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);
|
||||
};
|
||||
|
||||
|
||||
@@ -317,7 +318,7 @@ public:
|
||||
class CAddrDB
|
||||
{
|
||||
private:
|
||||
boost::filesystem::path pathAddr;
|
||||
std::filesystem::path pathAddr;
|
||||
public:
|
||||
CAddrDB();
|
||||
bool Write(const CAddrMan& addr);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -24,37 +24,48 @@
|
||||
#endif
|
||||
#include "notificationqueue.h"
|
||||
#include "addressindex.h"
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
// boost/filesystem/convenience.hpp removed in modern Boost; functionality is in filesystem.hpp
|
||||
#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
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
// Windows.h (transitively included) defines these as macros, clobbering Checkpoints:: enum values.
|
||||
#ifdef STRICT
|
||||
#undef STRICT
|
||||
#endif
|
||||
#ifdef ADVISORY
|
||||
#undef ADVISORY
|
||||
#endif
|
||||
#ifdef PERMISSIVE
|
||||
#undef PERMISSIVE
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
namespace fs = boost::filesystem;
|
||||
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 boost::thread_group* pScriptCheckThreads = NULL;
|
||||
static std::unique_ptr<std::vector<std::thread>> pScriptCheckThreads;
|
||||
|
||||
static void ThreadScriptCheck()
|
||||
{
|
||||
@@ -99,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
|
||||
}
|
||||
|
||||
@@ -168,7 +179,7 @@ void ThreadDeferredStartup(void* parg)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
PrintExceptionContinue(NULL, "ThreadDeferredStartup()");
|
||||
PrintExceptionContinue(nullptr, "ThreadDeferredStartup()");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -223,12 +234,11 @@ void Shutdown(void* parg)
|
||||
pScriptCheckQueue->Quit();
|
||||
if (pScriptCheckThreads)
|
||||
{
|
||||
pScriptCheckThreads->join_all();
|
||||
delete pScriptCheckThreads;
|
||||
pScriptCheckThreads = NULL;
|
||||
for (std::thread& t : *pScriptCheckThreads)
|
||||
if (t.joinable()) t.join();
|
||||
pScriptCheckThreads.reset();
|
||||
}
|
||||
delete pScriptCheckQueue;
|
||||
pScriptCheckQueue = NULL;
|
||||
pScriptCheckQueue.reset();
|
||||
}
|
||||
|
||||
// NOW safe to destroy Tor state - all threads have stopped
|
||||
@@ -240,24 +250,24 @@ void Shutdown(void* parg)
|
||||
{
|
||||
pzmqNotifier->Shutdown();
|
||||
delete pzmqNotifier;
|
||||
pzmqNotifier = NULL;
|
||||
pzmqNotifier = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pNotificationQueue)
|
||||
{
|
||||
delete pNotificationQueue;
|
||||
pNotificationQueue = NULL;
|
||||
pNotificationQueue = nullptr;
|
||||
}
|
||||
|
||||
// CTxDB().Close();
|
||||
// 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;
|
||||
@@ -307,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);
|
||||
|
||||
@@ -329,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)
|
||||
@@ -343,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;
|
||||
}
|
||||
|
||||
@@ -407,7 +417,7 @@ std::string HelpMessage()
|
||||
//" -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" +
|
||||
//" -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" +
|
||||
" -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"
|
||||
" -notor " + _("Disable Tor (WARNING: wallet will not start - Tor is required)") + "\n" +
|
||||
" -notor " + _("Disable Tor - run in clearnet-only mode (no .onion connectivity)") + "\n" +
|
||||
" -torsocks=<port> " + _("Set embedded or managed Tor SOCKS proxy port (default: 19099)") + "\n" +
|
||||
" -torhiddenservice " + _("Enable the managed Tor hidden service (default: 1)") + "\n" +
|
||||
" -torhsport=<port> " + _("Set embedded or managed Tor hidden service port (default: wallet listen port)") + "\n" +
|
||||
@@ -469,7 +479,6 @@ std::string HelpMessage()
|
||||
" -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" +
|
||||
" -confchange " + _("Require a confirmations for change (default: 0)") + "\n" +
|
||||
" -enforcecanonical " + _("Enforce transaction scripts to use canonical PUSH operators (default: 1)") + "\n" +
|
||||
" -alertnotify=<cmd> " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" +
|
||||
" -upgradewallet " + _("Upgrade wallet to latest format") + "\n" +
|
||||
" -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n" +
|
||||
" -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" +
|
||||
@@ -532,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
|
||||
@@ -549,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);
|
||||
@@ -559,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
|
||||
@@ -577,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;
|
||||
@@ -691,15 +700,15 @@ bool AppInit2()
|
||||
|
||||
int nScriptCheckThreads = GetArg("-par", 0);
|
||||
if (nScriptCheckThreads <= 0)
|
||||
nScriptCheckThreads = boost::thread::hardware_concurrency();
|
||||
nScriptCheckThreads = std::thread::hardware_concurrency();
|
||||
if (nScriptCheckThreads > 16)
|
||||
nScriptCheckThreads = 16;
|
||||
if (nScriptCheckThreads > 1)
|
||||
{
|
||||
pScriptCheckQueue = new CCheckQueue<CScriptCheck>(32);
|
||||
pScriptCheckThreads = new boost::thread_group();
|
||||
pScriptCheckQueue = std::make_unique<CCheckQueue<CScriptCheck>>(32);
|
||||
pScriptCheckThreads = std::make_unique<std::vector<std::thread>>();
|
||||
for (int i = 0; i < nScriptCheckThreads - 1; ++i)
|
||||
pScriptCheckThreads->create_thread(&ThreadScriptCheck);
|
||||
pScriptCheckThreads->emplace_back(&ThreadScriptCheck);
|
||||
printf("Script verification threads: %d workers + main thread\n", nScriptCheckThreads - 1);
|
||||
}
|
||||
|
||||
@@ -719,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())
|
||||
@@ -903,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"));
|
||||
}
|
||||
|
||||
@@ -918,7 +927,8 @@ bool AppInit2()
|
||||
// v5.9.5: P2P UTXO snapshot fetch is the default for fresh installs (Step 11.6).
|
||||
// The legacy clearnet HTTP bootstrap only runs when the user explicitly requests
|
||||
// it via -bootstrap, or when -snapshot=0 disables the P2P fetcher.
|
||||
#ifndef QT_GUI
|
||||
// Bootstrap auto-download works for both GUI and daemon.
|
||||
// GUI users get the same automatic bootstrap on fresh installs.
|
||||
{
|
||||
bool wantsBootstrap = GetBoolArg("-bootstrap", false);
|
||||
bool noBootstrap = GetBoolArg("-nobootstrap", false);
|
||||
@@ -929,6 +939,7 @@ bool AppInit2()
|
||||
if (needsBootstrap && !noBootstrap && !snapshotMode) {
|
||||
printf("Bootstrap: no blockchain data found — downloading automatically.\n");
|
||||
printf("Bootstrap: (use -nobootstrap to skip)\n");
|
||||
uiInterface.InitMessage(_("Downloading blockchain data..."));
|
||||
wantsBootstrap = true;
|
||||
} else if (needsBootstrap && snapshotMode && !wantsBootstrap) {
|
||||
printf("Bootstrap: no blockchain data found — will fetch UTXO snapshot via P2P after network start.\n");
|
||||
@@ -942,20 +953,32 @@ bool AppInit2()
|
||||
std::string host = Bootstrap::DEFAULT_HOST;
|
||||
std::string strError;
|
||||
|
||||
auto progressFn = [](int64_t bytesDownloaded, int64_t totalBytes) {
|
||||
int64_t lastGuiUpdate = 0;
|
||||
auto progressFn = [&lastGuiUpdate](int64_t bytesDownloaded, int64_t totalBytes) {
|
||||
if (totalBytes > 0) {
|
||||
printf("\rBootstrap: %lld / %lld MB (%lld%%)",
|
||||
(long long)(bytesDownloaded / (1024*1024)),
|
||||
(long long)(totalBytes / (1024*1024)),
|
||||
(long long)((bytesDownloaded * 100) / totalBytes));
|
||||
fflush(stdout);
|
||||
// Update GUI status bar every ~1 MB
|
||||
int64_t now = GetTimeMillis();
|
||||
if (now - lastGuiUpdate > 1000) {
|
||||
lastGuiUpdate = now;
|
||||
std::string msg = strprintf("Downloading blockchain: %lld / %lld MB (%lld%%)",
|
||||
(long long)(bytesDownloaded / (1024*1024)),
|
||||
(long long)(totalBytes / (1024*1024)),
|
||||
(long long)((bytesDownloaded * 100) / totalBytes));
|
||||
uiInterface.InitMessage(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Try UTXO snapshot first (fast: ~2-10 MB download)
|
||||
// Try UTXO snapshot first (fast: ~2-10 MB download). Only attempted
|
||||
// if the configured backend's chain DB doesn't already exist.
|
||||
bool success = false;
|
||||
bool triedUtxoSnapshot = false;
|
||||
if (needsBootstrap && !fs::exists(dataPath / "txleveldb")) {
|
||||
if (needsBootstrap && !fs::exists(GetChainDataDir())) {
|
||||
uiInterface.InitMessage(_("Downloading UTXO snapshot..."));
|
||||
printf("Bootstrap: trying UTXO snapshot from %s (fast path)...\n", host.c_str());
|
||||
|
||||
@@ -989,16 +1012,16 @@ bool AppInit2()
|
||||
strprintf("host=%s success=%d utxo_snapshot=%d", host.c_str(), success, triedUtxoSnapshot));
|
||||
}
|
||||
} // end bootstrap scope
|
||||
#endif
|
||||
|
||||
// ********************************************************* Step 6c: manual UTXO snapshot loading
|
||||
// If utxo-snapshot.bin exists in data dir and no txleveldb, load it.
|
||||
// If utxo-snapshot.bin exists in data dir and the chain DB hasn't been
|
||||
// initialized for the configured backend, load it.
|
||||
{
|
||||
fs::path dataPath = GetDataDir();
|
||||
fs::path snapshotFile = dataPath / "utxo-snapshot.bin";
|
||||
fs::path txleveldbDir = dataPath / "txleveldb";
|
||||
fs::path chainDbDir = GetChainDataDir();
|
||||
|
||||
if (fs::exists(snapshotFile) && !fs::exists(txleveldbDir)) {
|
||||
if (fs::exists(snapshotFile) && !fs::exists(chainDbDir)) {
|
||||
printf("Found utxo-snapshot.bin — loading UTXO snapshot...\n");
|
||||
uiInterface.InitMessage(_("Loading UTXO snapshot..."));
|
||||
|
||||
@@ -1012,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()))
|
||||
@@ -1024,22 +1057,22 @@ bool AppInit2()
|
||||
|
||||
if (GetBoolArg("-loadblockindextest"))
|
||||
{
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
txdb.LoadBlockIndex();
|
||||
PrintBlockTree();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle -reindex: delete the LevelDB block index so it gets rebuilt
|
||||
// from the raw blk*.dat files via FastImportBlockFile().
|
||||
// This recalculates money supply, tx index, and UTXO set from scratch.
|
||||
// Handle -reindex: delete the chain DB so it gets rebuilt from the raw
|
||||
// blk*.dat files via FastImportBlockFile(). This recalculates money
|
||||
// supply, tx index, and UTXO set from scratch. Backend-agnostic via
|
||||
// WipeChainDataDir(), which resolves the directory per the configured
|
||||
// -chaindb backend.
|
||||
if (GetBoolArg("-reindex", false))
|
||||
{
|
||||
printf("Reindex requested: removing block index database...\n");
|
||||
uiInterface.InitMessage(_("Removing block index for reindex..."));
|
||||
fs::path txleveldbPath = GetDataDir() / "txleveldb";
|
||||
if (fs::exists(txleveldbPath))
|
||||
fs::remove_all(txleveldbPath);
|
||||
printf("Reindex requested: removing chain database...\n");
|
||||
uiInterface.InitMessage(_("Removing chain database for reindex..."));
|
||||
WipeChainDataDir();
|
||||
}
|
||||
|
||||
uiInterface.InitMessage(_("Loading block index..."));
|
||||
@@ -1048,10 +1081,41 @@ 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.
|
||||
if (nBestHeight == 0 && boost::filesystem::exists(GetDataDir() / "blk0001.dat")
|
||||
if (nBestHeight == 0 && std::filesystem::exists(GetDataDir() / "blk0001.dat")
|
||||
&& mapBlockIndex.size() <= 1)
|
||||
{
|
||||
uiInterface.InitMessage(_("Importing bootstrap blocks..."));
|
||||
@@ -1130,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)
|
||||
{
|
||||
@@ -1174,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);
|
||||
@@ -1202,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"))
|
||||
@@ -1224,7 +1288,7 @@ bool AppInit2()
|
||||
bool fScannedWithIndex = false;
|
||||
if (fAddressIndex && !GetBoolArg("-rescan"))
|
||||
{
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
int nAddressIndexStartHeight = 0;
|
||||
uint256 hashAddressIndexBestChain = 0;
|
||||
if (txdb.ReadAddressIndexStartHeight(nAddressIndexStartHeight) &&
|
||||
@@ -1322,6 +1386,15 @@ bool AppInit2()
|
||||
#ifdef USE_UPNP
|
||||
fUseUPnP = false;
|
||||
#endif
|
||||
} else if (GetBoolArg("-notor", false)) {
|
||||
// -notor: user explicitly disabled Tor. Allow the daemon to start
|
||||
// in clearnet-only mode (useful for diagnostics, benchmarking, and
|
||||
// recovery). .onion connectivity will not be available.
|
||||
printf("NOTICE: Tor disabled via -notor. Running in clearnet-only mode.\n");
|
||||
printf(" .onion connections will NOT be available.\n");
|
||||
SetReachable(NET_IPV4, true);
|
||||
SetReachable(NET_IPV6, true);
|
||||
SetReachable(NET_TOR, false);
|
||||
} else {
|
||||
std::string torError = CTorEmbedded::GetInstance()->GetStartupError();
|
||||
if (torError.empty())
|
||||
@@ -1392,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");
|
||||
}
|
||||
}
|
||||
@@ -1460,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
|
||||
@@ -1480,7 +1553,7 @@ bool AppInit2()
|
||||
if (snapshotMode && needsSnapshot && !haveSnapshotFile &&
|
||||
Checkpoints::GetBestSnapshotHeight() > 0)
|
||||
{
|
||||
NewThread(ThreadSnapshotFetch, NULL);
|
||||
NewThread(ThreadSnapshotFetch, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1488,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();
|
||||
@@ -1510,7 +1583,7 @@ bool AppInit2()
|
||||
{
|
||||
printf("ZMQ: Failed to initialize publisher on %s\n", zmqAddr.c_str());
|
||||
delete pzmqNotifier;
|
||||
pzmqNotifier = NULL;
|
||||
pzmqNotifier = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "irc.h"
|
||||
#include "net.h"
|
||||
#include "strlcpy.h"
|
||||
#include "base58.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
|
||||
int nGotIRCAddresses = 0;
|
||||
|
||||
void ThreadIRCSeed2(void* parg);
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ircaddr
|
||||
{
|
||||
struct in_addr ip;
|
||||
short port;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
string EncodeAddress(const CService& addr)
|
||||
{
|
||||
struct ircaddr tmp;
|
||||
if (addr.GetInAddr(&tmp.ip))
|
||||
{
|
||||
tmp.port = htons(addr.GetPort());
|
||||
|
||||
vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
|
||||
return string("u") + EncodeBase58Check(vch);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool DecodeAddress(string str, CService& addr)
|
||||
{
|
||||
vector<unsigned char> vch;
|
||||
if (!DecodeBase58Check(str.substr(1), vch))
|
||||
return false;
|
||||
|
||||
struct ircaddr tmp;
|
||||
if (vch.size() != sizeof(tmp))
|
||||
return false;
|
||||
memcpy(&tmp, &vch[0], sizeof(tmp));
|
||||
|
||||
addr = CService(tmp.ip, ntohs(tmp.port));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static bool Send(SOCKET hSocket, const char* pszSend)
|
||||
{
|
||||
if (strstr(pszSend, "PONG") != pszSend)
|
||||
printf("IRC SENDING: %s\n", pszSend);
|
||||
const char* psz = pszSend;
|
||||
const char* pszEnd = psz + strlen(psz);
|
||||
while (psz < pszEnd)
|
||||
{
|
||||
int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
psz += ret;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecvLineIRC(SOCKET hSocket, string& strLine)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
bool fRet = RecvLine(hSocket, strLine);
|
||||
if (fRet)
|
||||
{
|
||||
if (fShutdown)
|
||||
return false;
|
||||
vector<string> vWords;
|
||||
ParseString(strLine, ' ', vWords);
|
||||
if (vWords.size() >= 1 && vWords[0] == "PING")
|
||||
{
|
||||
strLine[1] = 'O';
|
||||
strLine += '\r';
|
||||
Send(hSocket, strLine.c_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return fRet;
|
||||
}
|
||||
}
|
||||
|
||||
int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
string strLine;
|
||||
strLine.reserve(10000);
|
||||
if (!RecvLineIRC(hSocket, strLine))
|
||||
return 0;
|
||||
printf("IRC %s\n", strLine.c_str());
|
||||
if (psz1 && strLine.find(psz1) != string::npos)
|
||||
return 1;
|
||||
if (psz2 && strLine.find(psz2) != string::npos)
|
||||
return 2;
|
||||
if (psz3 && strLine.find(psz3) != string::npos)
|
||||
return 3;
|
||||
if (psz4 && strLine.find(psz4) != string::npos)
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
bool Wait(int nSeconds)
|
||||
{
|
||||
if (fShutdown)
|
||||
return false;
|
||||
printf("IRC waiting %d seconds to reconnect\n", nSeconds);
|
||||
for (int i = 0; i < nSeconds; i++)
|
||||
{
|
||||
if (fShutdown)
|
||||
return false;
|
||||
MilliSleep(1000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
|
||||
{
|
||||
strRet.clear();
|
||||
while (true)
|
||||
{
|
||||
string strLine;
|
||||
if (!RecvLineIRC(hSocket, strLine))
|
||||
return false;
|
||||
|
||||
vector<string> vWords;
|
||||
ParseString(strLine, ' ', vWords);
|
||||
if (vWords.size() < 2)
|
||||
continue;
|
||||
|
||||
if (vWords[1] == psz1)
|
||||
{
|
||||
printf("IRC %s\n", strLine.c_str());
|
||||
strRet = strLine;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GetIPFromIRC(SOCKET hSocket, string strMyName, CNetAddr& ipRet)
|
||||
{
|
||||
Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str());
|
||||
|
||||
string strLine;
|
||||
if (!RecvCodeLine(hSocket, "302", strLine))
|
||||
return false;
|
||||
|
||||
vector<string> vWords;
|
||||
ParseString(strLine, ' ', vWords);
|
||||
if (vWords.size() < 4)
|
||||
return false;
|
||||
|
||||
string str = vWords[3];
|
||||
if (str.rfind("@") == string::npos)
|
||||
return false;
|
||||
string strHost = str.substr(str.rfind("@")+1);
|
||||
|
||||
// Hybrid IRC used by lfnet always returns IP when you userhost yourself,
|
||||
// but in case another IRC is ever used this should work.
|
||||
printf("GetIPFromIRC() got userhost %s\n", strHost.c_str());
|
||||
CNetAddr addr(strHost, true);
|
||||
if (!addr.IsValid())
|
||||
return false;
|
||||
ipRet = addr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ThreadIRCSeed(void* parg)
|
||||
{
|
||||
// Make this thread recognisable as the IRC seeding thread
|
||||
RenameThread("Triangles-ircseed");
|
||||
|
||||
try
|
||||
{
|
||||
ThreadIRCSeed2(parg);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
PrintExceptionContinue(&e, "ThreadIRCSeed()");
|
||||
} catch (...) {
|
||||
PrintExceptionContinue(NULL, "ThreadIRCSeed()");
|
||||
}
|
||||
printf("ThreadIRCSeed exited\n");
|
||||
}
|
||||
|
||||
void ThreadIRCSeed2(void* parg)
|
||||
{
|
||||
// Don't connect to IRC if we won't use IPv4 connections.
|
||||
if (IsLimited(NET_IPV4))
|
||||
return;
|
||||
|
||||
// ... or if we won't make outbound connections and won't accept inbound ones.
|
||||
if (mapArgs.count("-connect") && fNoListen)
|
||||
return;
|
||||
|
||||
// ... or if IRC is not enabled.
|
||||
if (!GetBoolArg("-irc", false))
|
||||
return;
|
||||
|
||||
printf("ThreadIRCSeed started\n");
|
||||
int nErrorWait = 10;
|
||||
int nRetryWait = 10;
|
||||
int nNameRetry = 0;
|
||||
|
||||
while (!fShutdown)
|
||||
{
|
||||
CService addrConnect("92.243.23.21", 6667); // irc.lfnet.org
|
||||
|
||||
CService addrIRC("irc.lfnet.org", 6667, true);
|
||||
if (addrIRC.IsValid())
|
||||
addrConnect = addrIRC;
|
||||
|
||||
SOCKET hSocket;
|
||||
if (!ConnectSocket(addrConnect, hSocket))
|
||||
{
|
||||
printf("IRC connect failed\n");
|
||||
nErrorWait = nErrorWait * 11 / 10;
|
||||
if (Wait(nErrorWait += 60))
|
||||
continue;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
|
||||
{
|
||||
closesocket(hSocket);
|
||||
hSocket = INVALID_SOCKET;
|
||||
nErrorWait = nErrorWait * 11 / 10;
|
||||
if (Wait(nErrorWait += 60))
|
||||
continue;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses
|
||||
CService addrLocal;
|
||||
string strMyName;
|
||||
// Don't use our IP as our nick if we're not listening
|
||||
// or if it keeps failing because the nick is already in use.
|
||||
if (!fNoListen && GetLocal(addrLocal, &addrIPv4) && nNameRetry<3)
|
||||
strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
|
||||
if (strMyName == "")
|
||||
strMyName = strprintf("x%" PRIu64 "", GetRand(1000000000));
|
||||
|
||||
Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
|
||||
Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str());
|
||||
|
||||
int nRet = RecvUntil(hSocket, " 004 ", " 433 ");
|
||||
if (nRet != 1)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
hSocket = INVALID_SOCKET;
|
||||
if (nRet == 2)
|
||||
{
|
||||
printf("IRC name already in use\n");
|
||||
nNameRetry++;
|
||||
Wait(10);
|
||||
continue;
|
||||
}
|
||||
nErrorWait = nErrorWait * 11 / 10;
|
||||
if (Wait(nErrorWait += 60))
|
||||
continue;
|
||||
else
|
||||
return;
|
||||
}
|
||||
nNameRetry = 0;
|
||||
MilliSleep(500);
|
||||
|
||||
// Get our external IP from the IRC server and re-nick before joining the channel
|
||||
CNetAddr addrFromIRC;
|
||||
if (GetIPFromIRC(hSocket, strMyName, addrFromIRC))
|
||||
{
|
||||
printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToString().c_str());
|
||||
// Don't use our IP as our nick if we're not listening
|
||||
if (!fNoListen && addrFromIRC.IsRoutable())
|
||||
{
|
||||
// IRC lets you to re-nick
|
||||
AddLocal(addrFromIRC, LOCAL_IRC);
|
||||
strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
|
||||
Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (fTestNet) {
|
||||
Send(hSocket, "JOIN #TrianglesTEST\r");
|
||||
Send(hSocket, "WHO #TrianglesTEST\r");
|
||||
} else {
|
||||
// randomly join
|
||||
// int channel_number = GetRandInt(5);
|
||||
|
||||
// Channel number is always 0 for initial release
|
||||
int channel_number = 0;
|
||||
Send(hSocket, strprintf("JOIN #Triangles%02d\r", channel_number).c_str());
|
||||
Send(hSocket, strprintf("WHO #Triangles%02d\r", channel_number).c_str());
|
||||
}
|
||||
|
||||
int64_t nStart = GetTime();
|
||||
string strLine;
|
||||
strLine.reserve(10000);
|
||||
while (!fShutdown && RecvLineIRC(hSocket, strLine))
|
||||
{
|
||||
if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':')
|
||||
continue;
|
||||
|
||||
vector<string> vWords;
|
||||
ParseString(strLine, ' ', vWords);
|
||||
if (vWords.size() < 2)
|
||||
continue;
|
||||
|
||||
char pszName[10000];
|
||||
pszName[0] = '\0';
|
||||
|
||||
if (vWords[1] == "352" && vWords.size() >= 8)
|
||||
{
|
||||
// index 7 is limited to 16 characters
|
||||
// could get full length name at index 10, but would be different from join messages
|
||||
strlcpy(pszName, vWords[7].c_str(), sizeof(pszName));
|
||||
printf("IRC got who\n");
|
||||
}
|
||||
|
||||
if (vWords[1] == "JOIN" && vWords[0].size() > 1)
|
||||
{
|
||||
// :username!username@50000007.F000000B.90000002.IP JOIN :#channelname
|
||||
strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
|
||||
if (strchr(pszName, '!'))
|
||||
*strchr(pszName, '!') = '\0';
|
||||
printf("IRC got join\n");
|
||||
}
|
||||
|
||||
if (pszName[0] == 'u')
|
||||
{
|
||||
CAddress addr;
|
||||
if (DecodeAddress(pszName, addr))
|
||||
{
|
||||
addr.nTime = GetAdjustedTime();
|
||||
if (addrman.Add(addr, addrConnect, 51 * 60))
|
||||
printf("IRC got new address: %s\n", addr.ToString().c_str());
|
||||
nGotIRCAddresses++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("IRC decode failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
closesocket(hSocket);
|
||||
hSocket = INVALID_SOCKET;
|
||||
|
||||
if (GetTime() - nStart > 20 * 60)
|
||||
{
|
||||
nErrorWait /= 3;
|
||||
nRetryWait /= 3;
|
||||
}
|
||||
|
||||
nRetryWait = nRetryWait * 11 / 10;
|
||||
if (!Wait(nRetryWait += 60))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef TEST
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
WSADATA wsadata;
|
||||
if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR)
|
||||
{
|
||||
printf("Error at WSAStartup()\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ThreadIRCSeed(NULL);
|
||||
|
||||
WSACleanup();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#ifndef TRIANGLES_IRC_H
|
||||
#define TRIANGLES_IRC_H
|
||||
|
||||
void ThreadIRCSeed(void* parg);
|
||||
|
||||
extern int nGotIRCAddresses;
|
||||
|
||||
#endif
|
||||
@@ -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);
|
||||
}
|
||||
@@ -386,7 +396,7 @@ bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hash
|
||||
const CTxIn& txin = tx.vin[0];
|
||||
|
||||
// First try finding the previous transaction in database
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
CTransaction txPrev;
|
||||
CTxIndex txindex;
|
||||
if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
|
||||
|
||||
@@ -1,213 +1,33 @@
|
||||
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||
// 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 <map>
|
||||
#include <cstring>
|
||||
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
#include <openssl/crypto.h> // OPENSSL_cleanse for secure wipe of secret bytes
|
||||
#include <openssl/rand.h> // RAND_bytes for new-key entropy
|
||||
|
||||
#include "crypto_ecdsa.h"
|
||||
#include "key.h"
|
||||
|
||||
// Generate a private key from just the secret parameter
|
||||
int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Order-of-generator constants (still used by CheckSignatureElement, the only
|
||||
// caller into the BigEndian comparison helper below). Kept here so the file
|
||||
// remains self-contained.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
namespace {
|
||||
|
||||
int CompareBigEndian(const unsigned char* c1, std::size_t c1len,
|
||||
const unsigned char* c2, std::size_t c2len)
|
||||
{
|
||||
int ok = 0;
|
||||
BN_CTX *ctx = NULL;
|
||||
EC_POINT *pub_key = NULL;
|
||||
|
||||
if (!eckey) return 0;
|
||||
|
||||
const EC_GROUP *group = EC_KEY_get0_group(eckey);
|
||||
|
||||
if ((ctx = BN_CTX_new()) == NULL)
|
||||
goto err;
|
||||
|
||||
pub_key = EC_POINT_new(group);
|
||||
|
||||
if (pub_key == NULL)
|
||||
goto err;
|
||||
|
||||
if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
|
||||
goto err;
|
||||
|
||||
EC_KEY_set_private_key(eckey,priv_key);
|
||||
EC_KEY_set_public_key(eckey,pub_key);
|
||||
|
||||
ok = 1;
|
||||
|
||||
err:
|
||||
|
||||
if (pub_key)
|
||||
EC_POINT_free(pub_key);
|
||||
if (ctx != NULL)
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
return(ok);
|
||||
}
|
||||
|
||||
// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
|
||||
// recid selects which key is recovered
|
||||
// if check is non-zero, additional checks are performed
|
||||
int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
|
||||
{
|
||||
if (!eckey) return 0;
|
||||
|
||||
int ret = 0;
|
||||
BN_CTX *ctx = NULL;
|
||||
|
||||
BIGNUM *x = NULL;
|
||||
BIGNUM *e = NULL;
|
||||
BIGNUM *order = NULL;
|
||||
BIGNUM *sor = NULL;
|
||||
BIGNUM *eor = NULL;
|
||||
BIGNUM *field = NULL;
|
||||
EC_POINT *R = NULL;
|
||||
EC_POINT *O = NULL;
|
||||
EC_POINT *Q = NULL;
|
||||
BIGNUM *rr = NULL;
|
||||
BIGNUM *zero = NULL;
|
||||
int n = 0;
|
||||
int i = recid / 2;
|
||||
|
||||
const EC_GROUP *group = EC_KEY_get0_group(eckey);
|
||||
if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
|
||||
BN_CTX_start(ctx);
|
||||
const BIGNUM *sig_r, *sig_s;
|
||||
ECDSA_SIG_get0(ecsig, &sig_r, &sig_s);
|
||||
order = BN_CTX_get(ctx);
|
||||
if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
|
||||
x = BN_CTX_get(ctx);
|
||||
if (!BN_copy(x, order)) { ret=-1; goto err; }
|
||||
if (!BN_mul_word(x, i)) { ret=-1; goto err; }
|
||||
if (!BN_add(x, x, sig_r)) { ret=-1; goto err; }
|
||||
field = BN_CTX_get(ctx);
|
||||
if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
|
||||
if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
|
||||
if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
|
||||
if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
|
||||
if (check)
|
||||
{
|
||||
if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
|
||||
if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
|
||||
if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
|
||||
}
|
||||
if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
|
||||
n = EC_GROUP_get_degree(group);
|
||||
e = BN_CTX_get(ctx);
|
||||
if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
|
||||
if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
|
||||
zero = BN_CTX_get(ctx);
|
||||
BN_zero(zero);
|
||||
if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
|
||||
rr = BN_CTX_get(ctx);
|
||||
if (!BN_mod_inverse(rr, sig_r, order, ctx)) { ret=-1; goto err; }
|
||||
sor = BN_CTX_get(ctx);
|
||||
if (!BN_mod_mul(sor, sig_s, rr, order, ctx)) { ret=-1; goto err; }
|
||||
eor = BN_CTX_get(ctx);
|
||||
if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
|
||||
if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
|
||||
if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
|
||||
|
||||
ret = 1;
|
||||
|
||||
err:
|
||||
if (ctx) {
|
||||
BN_CTX_end(ctx);
|
||||
BN_CTX_free(ctx);
|
||||
}
|
||||
if (R != NULL) EC_POINT_free(R);
|
||||
if (O != NULL) EC_POINT_free(O);
|
||||
if (Q != NULL) EC_POINT_free(Q);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CKey::SetCompressedPubKey()
|
||||
{
|
||||
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
|
||||
fCompressedPubKey = true;
|
||||
}
|
||||
|
||||
void CKey::SetUnCompressedPubKey()
|
||||
{
|
||||
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_UNCOMPRESSED);
|
||||
fCompressedPubKey = false;
|
||||
}
|
||||
|
||||
EC_KEY* CKey::GetECKey()
|
||||
{
|
||||
return pkey;
|
||||
}
|
||||
|
||||
void CKey::Reset()
|
||||
{
|
||||
fCompressedPubKey = false;
|
||||
if (pkey != NULL)
|
||||
EC_KEY_free(pkey);
|
||||
pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
if (pkey == NULL)
|
||||
throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed");
|
||||
fSet = false;
|
||||
}
|
||||
|
||||
CKey::CKey()
|
||||
{
|
||||
pkey = NULL;
|
||||
Reset();
|
||||
}
|
||||
|
||||
CKey::CKey(const CKey& b)
|
||||
{
|
||||
pkey = EC_KEY_dup(b.pkey);
|
||||
if (pkey == NULL)
|
||||
throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed");
|
||||
fSet = b.fSet;
|
||||
}
|
||||
|
||||
CKey& CKey::operator=(const CKey& b)
|
||||
{
|
||||
if (!EC_KEY_copy(pkey, b.pkey))
|
||||
throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed");
|
||||
fSet = b.fSet;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
CKey::~CKey()
|
||||
{
|
||||
EC_KEY_free(pkey);
|
||||
}
|
||||
|
||||
bool CKey::IsNull() const
|
||||
{
|
||||
return !fSet;
|
||||
}
|
||||
|
||||
bool CKey::IsCompressed() const
|
||||
{
|
||||
return fCompressedPubKey;
|
||||
}
|
||||
|
||||
int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) {
|
||||
while (c1len > c2len) {
|
||||
if (*c1)
|
||||
return 1;
|
||||
c1++;
|
||||
c1len--;
|
||||
}
|
||||
while (c2len > c1len) {
|
||||
if (*c2)
|
||||
return -1;
|
||||
c2++;
|
||||
c2len--;
|
||||
}
|
||||
while (c1len > c2len) { if (*c1) return 1; c1++; c1len--; }
|
||||
while (c2len > c1len) { if (*c2) return -1; c2++; c2len--; }
|
||||
while (c1len > 0) {
|
||||
if (*c1 > *c2)
|
||||
return 1;
|
||||
if (*c2 > *c1)
|
||||
return -1;
|
||||
c1++;
|
||||
c2++;
|
||||
c1len--;
|
||||
if (*c1 > *c2) return 1;
|
||||
if (*c2 > *c1) return -1;
|
||||
c1++; c2++; c1len--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -228,277 +48,332 @@ const unsigned char vchMaxModHalfOrder[32] = {
|
||||
0xDF,0xE9,0x2F,0x46,0x68,0x1B,0x20,0xA0
|
||||
};
|
||||
|
||||
const unsigned char vchZero[0] = {};
|
||||
const unsigned char vchZero[1] = { 0 };
|
||||
|
||||
bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) {
|
||||
return CompareBigEndian(vch, len, vchZero, 0) > 0 &&
|
||||
CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0;
|
||||
} // namespace
|
||||
|
||||
bool CKey::CheckSignatureElement(const unsigned char* vchIn, int len, bool half)
|
||||
{
|
||||
return CompareBigEndian(vchIn, len, vchZero, 0) > 0 &&
|
||||
CompareBigEndian(vchIn, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Lifecycle
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
void CKey::Reset()
|
||||
{
|
||||
OPENSSL_cleanse(vch, sizeof(vch));
|
||||
vchPubKey.clear();
|
||||
fSet = false;
|
||||
fHavePrivKey = false;
|
||||
fCompressedPubKey = false;
|
||||
}
|
||||
|
||||
CKey::CKey()
|
||||
{
|
||||
std::memset(vch, 0, sizeof(vch));
|
||||
vchPubKey.clear();
|
||||
fSet = false;
|
||||
fHavePrivKey = false;
|
||||
fCompressedPubKey = false;
|
||||
}
|
||||
|
||||
CKey::CKey(const CKey& b)
|
||||
{
|
||||
*this = b;
|
||||
}
|
||||
|
||||
CKey& CKey::operator=(const CKey& b)
|
||||
{
|
||||
if (this == &b) return *this;
|
||||
std::memcpy(vch, b.vch, sizeof(vch));
|
||||
vchPubKey = b.vchPubKey;
|
||||
fSet = b.fSet;
|
||||
fHavePrivKey = b.fHavePrivKey;
|
||||
fCompressedPubKey = b.fCompressedPubKey;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CKey::~CKey()
|
||||
{
|
||||
OPENSSL_cleanse(vch, sizeof(vch));
|
||||
}
|
||||
|
||||
bool CKey::IsNull() const { return !fSet; }
|
||||
bool CKey::IsCompressed() const { return fCompressedPubKey; }
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Compression toggle
|
||||
//
|
||||
// In the new model the pubkey is always cached at the current compression. If
|
||||
// we hold the private key we can re-derive trivially; if we only hold a public
|
||||
// key, callers don't toggle compression in practice in this codebase, so we
|
||||
// just flip the flag and rely on the next SetPubKey/SetSecret to refresh the
|
||||
// cache.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
void CKey::SetCompressedPubKey()
|
||||
{
|
||||
if (fCompressedPubKey) return;
|
||||
fCompressedPubKey = true;
|
||||
if (fSet && fHavePrivKey) {
|
||||
std::size_t len = 33;
|
||||
vchPubKey.resize(len);
|
||||
if (!ECDSA_pubkey_from_privkey_secp256k1(&vchPubKey[0], &len, vch, /*fCompressed=*/true)) {
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
vchPubKey.resize(len);
|
||||
}
|
||||
}
|
||||
|
||||
void CKey::SetUnCompressedPubKey()
|
||||
{
|
||||
if (!fCompressedPubKey && fSet) return;
|
||||
fCompressedPubKey = false;
|
||||
if (fSet && fHavePrivKey) {
|
||||
std::size_t len = 65;
|
||||
vchPubKey.resize(len);
|
||||
if (!ECDSA_pubkey_from_privkey_secp256k1(&vchPubKey[0], &len, vch, /*fCompressed=*/false)) {
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
vchPubKey.resize(len);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Key generation / load / store
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
void CKey::MakeNewKey(bool fCompressed)
|
||||
{
|
||||
if (!EC_KEY_generate_key(pkey))
|
||||
throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed");
|
||||
if (fCompressed)
|
||||
SetCompressedPubKey();
|
||||
fSet = true;
|
||||
// Sample 32 bytes of entropy and reject any that fall outside (0, n).
|
||||
// Probability of needing a retry is ~2^-128.
|
||||
do {
|
||||
if (RAND_bytes(vch, sizeof(vch)) != 1)
|
||||
throw key_error("CKey::MakeNewKey() : RAND_bytes failed");
|
||||
} while (!ECDSA_seckey_verify_secp256k1(vch));
|
||||
|
||||
fSet = true;
|
||||
fHavePrivKey = true;
|
||||
fCompressedPubKey = fCompressed;
|
||||
|
||||
std::size_t len = fCompressed ? 33 : 65;
|
||||
vchPubKey.resize(len);
|
||||
if (!ECDSA_pubkey_from_privkey_secp256k1(&vchPubKey[0], &len, vch, fCompressed)) {
|
||||
Reset();
|
||||
throw key_error("CKey::MakeNewKey() : failed to derive public key");
|
||||
}
|
||||
vchPubKey.resize(len);
|
||||
}
|
||||
|
||||
bool CKey::SetPrivKey(const CPrivKey& vchPrivKey)
|
||||
{
|
||||
const unsigned char* pbegin = &vchPrivKey[0];
|
||||
if (d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size()))
|
||||
{
|
||||
// In testing, d2i_ECPrivateKey can return true
|
||||
// but fill in pkey with a key that fails
|
||||
// EC_KEY_check_key, so:
|
||||
if (EC_KEY_check_key(pkey))
|
||||
{
|
||||
fSet = true;
|
||||
return true;
|
||||
}
|
||||
unsigned char raw[32];
|
||||
if (!ECDSA_privkey_import_der_secp256k1(raw, &vchPrivKey[0], vchPrivKey.size())) {
|
||||
OPENSSL_cleanse(raw, sizeof(raw));
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
// If vchPrivKey data is bad d2i_ECPrivateKey() can
|
||||
// leave pkey in a state where calling EC_KEY_free()
|
||||
// crashes. To avoid that, set pkey to NULL and
|
||||
// leak the memory (a leak is better than a crash)
|
||||
pkey = NULL;
|
||||
Reset();
|
||||
return false;
|
||||
|
||||
// Carry the compressed flag out of the DER blob. The two valid sizes
|
||||
// produced by ECDSA_privkey_export_der_secp256k1 are 214 (compressed) and
|
||||
// 279 (uncompressed); foreign DER blobs are best-effort but those two
|
||||
// cover every record this codebase has ever written.
|
||||
bool fCompressed = (vchPrivKey.size() == 214);
|
||||
|
||||
CSecret secret(raw, raw + 32);
|
||||
OPENSSL_cleanse(raw, sizeof(raw));
|
||||
return SetSecret(secret, fCompressed);
|
||||
}
|
||||
|
||||
bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed)
|
||||
{
|
||||
EC_KEY_free(pkey);
|
||||
pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
if (pkey == NULL)
|
||||
throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed");
|
||||
if (vchSecret.size() != 32)
|
||||
throw key_error("CKey::SetSecret() : secret must be 32 bytes");
|
||||
BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new());
|
||||
if (bn == NULL)
|
||||
throw key_error("CKey::SetSecret() : BN_bin2bn failed");
|
||||
if (!EC_KEY_regenerate_key(pkey,bn))
|
||||
{
|
||||
BN_clear_free(bn);
|
||||
throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed");
|
||||
if (!ECDSA_seckey_verify_secp256k1(&vchSecret[0]))
|
||||
throw key_error("CKey::SetSecret() : secret is not a valid scalar");
|
||||
|
||||
std::memcpy(vch, &vchSecret[0], 32);
|
||||
fSet = true;
|
||||
fHavePrivKey = true;
|
||||
// Preserve sticky-compression behaviour from the OpenSSL implementation:
|
||||
// if either the explicit argument or the previously-set flag is true,
|
||||
// the result is compressed.
|
||||
bool fComp = fCompressed || fCompressedPubKey;
|
||||
fCompressedPubKey = fComp;
|
||||
|
||||
std::size_t len = fComp ? 33 : 65;
|
||||
vchPubKey.resize(len);
|
||||
if (!ECDSA_pubkey_from_privkey_secp256k1(&vchPubKey[0], &len, vch, fComp)) {
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
BN_clear_free(bn);
|
||||
fSet = true;
|
||||
if (fCompressed || fCompressedPubKey)
|
||||
SetCompressedPubKey();
|
||||
vchPubKey.resize(len);
|
||||
return true;
|
||||
}
|
||||
|
||||
CSecret CKey::GetSecret(bool &fCompressed) const
|
||||
CSecret CKey::GetSecret(bool& fCompressed) const
|
||||
{
|
||||
CSecret vchRet;
|
||||
vchRet.resize(32);
|
||||
const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
|
||||
int nBytes = BN_num_bytes(bn);
|
||||
if (bn == NULL)
|
||||
throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed");
|
||||
int n=BN_bn2bin(bn,&vchRet[32 - nBytes]);
|
||||
if (n != nBytes)
|
||||
throw key_error("CKey::GetSecret(): BN_bn2bin failed");
|
||||
if (!fSet || !fHavePrivKey)
|
||||
throw key_error("CKey::GetSecret() : key is not set or has no private component");
|
||||
CSecret out(vch, vch + 32);
|
||||
fCompressed = fCompressedPubKey;
|
||||
return vchRet;
|
||||
return out;
|
||||
}
|
||||
|
||||
CPrivKey CKey::GetPrivKey() const
|
||||
{
|
||||
int nSize = i2d_ECPrivateKey(pkey, NULL);
|
||||
if (!nSize)
|
||||
throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed");
|
||||
CPrivKey vchPrivKey(nSize, 0);
|
||||
unsigned char* pbegin = &vchPrivKey[0];
|
||||
if (i2d_ECPrivateKey(pkey, &pbegin) != nSize)
|
||||
throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size");
|
||||
return vchPrivKey;
|
||||
if (!fSet || !fHavePrivKey)
|
||||
throw key_error("CKey::GetPrivKey() : key is not set or has no private component");
|
||||
|
||||
// Max possible output: 279 bytes (uncompressed).
|
||||
CPrivKey out(279, 0);
|
||||
std::size_t out_len = out.size();
|
||||
if (!ECDSA_privkey_export_der_secp256k1(&out[0], &out_len, vch, fCompressedPubKey))
|
||||
throw key_error("CKey::GetPrivKey() : DER export failed");
|
||||
out.resize(out_len);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool CKey::SetPubKey(const CPubKey& vchPubKey)
|
||||
bool CKey::SetPubKey(const CPubKey& cpub)
|
||||
{
|
||||
const unsigned char* pbegin = &vchPubKey.vchPubKey[0];
|
||||
if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size()))
|
||||
{
|
||||
fSet = true;
|
||||
if (vchPubKey.vchPubKey.size() == 33)
|
||||
SetCompressedPubKey();
|
||||
return true;
|
||||
const std::vector<unsigned char>& vchPub = cpub.vchPubKey;
|
||||
if (vchPub.size() != 33 && vchPub.size() != 65) {
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
pkey = NULL;
|
||||
Reset();
|
||||
return false;
|
||||
if (!ECDSA_pubkey_verify_secp256k1(&vchPub[0], vchPub.size())) {
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
vchPubKey = vchPub;
|
||||
fSet = true;
|
||||
fHavePrivKey = false;
|
||||
fCompressedPubKey = (vchPub.size() == 33);
|
||||
return true;
|
||||
}
|
||||
|
||||
CPubKey CKey::GetPubKey() const
|
||||
{
|
||||
int nSize = i2o_ECPublicKey(pkey, NULL);
|
||||
if (!nSize)
|
||||
throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed");
|
||||
std::vector<unsigned char> vchPubKey(nSize, 0);
|
||||
unsigned char* pbegin = &vchPubKey[0];
|
||||
if (i2o_ECPublicKey(pkey, &pbegin) != nSize)
|
||||
throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size");
|
||||
return CPubKey(vchPubKey);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Sign / verify / recover (all delegate to crypto_ecdsa wrappers)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
bool CKey::Sign(uint256 hash, std::vector<unsigned char>& vchSig)
|
||||
{
|
||||
vchSig.clear();
|
||||
ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
|
||||
if (sig == NULL)
|
||||
if (!fSet || !fHavePrivKey) return false;
|
||||
|
||||
// libsecp256k1's max DER output is 72 bytes; allocate that and shrink.
|
||||
vchSig.resize(72);
|
||||
std::size_t sig_len = vchSig.size();
|
||||
if (!ECDSA_sign_secp256k1(&vchSig[0], &sig_len,
|
||||
reinterpret_cast<const unsigned char*>(&hash),
|
||||
vch))
|
||||
{
|
||||
vchSig.clear();
|
||||
return false;
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BN_CTX_start(ctx);
|
||||
const EC_GROUP *group = EC_KEY_get0_group(pkey);
|
||||
BIGNUM *order = BN_CTX_get(ctx);
|
||||
BIGNUM *halforder = BN_CTX_get(ctx);
|
||||
EC_GROUP_get_order(group, order, ctx);
|
||||
BN_rshift1(halforder, order);
|
||||
const BIGNUM *sig_r, *sig_s;
|
||||
ECDSA_SIG_get0(sig, &sig_r, &sig_s);
|
||||
if (BN_cmp(sig_s, halforder) > 0) {
|
||||
// enforce low S values, by negating the value (modulo the order) if above order/2.
|
||||
BIGNUM *new_s = BN_new();
|
||||
BN_sub(new_s, order, sig_s);
|
||||
BIGNUM *dup_r = BN_dup(sig_r);
|
||||
ECDSA_SIG_set0(sig, dup_r, new_s);
|
||||
}
|
||||
BN_CTX_end(ctx);
|
||||
BN_CTX_free(ctx);
|
||||
unsigned int nSize = ECDSA_size(pkey);
|
||||
vchSig.resize(nSize); // Make sure it is big enough
|
||||
unsigned char *pos = &vchSig[0];
|
||||
nSize = i2d_ECDSA_SIG(sig, &pos);
|
||||
ECDSA_SIG_free(sig);
|
||||
vchSig.resize(nSize); // Shrink to fit actual size
|
||||
vchSig.resize(sig_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
// create a compact signature (65 bytes), which allows reconstructing the used public key
|
||||
// The format is one header byte, followed by two times 32 bytes for the serialized r and s values.
|
||||
// The header byte: 0x1B = first key with even y, 0x1C = first key with odd y,
|
||||
// 0x1D = second key with even y, 0x1E = second key with odd y
|
||||
// Compact signature (65 bytes): one header byte (encoding recid + compression)
|
||||
// followed by 32-byte r and 32-byte s.
|
||||
bool CKey::SignCompact(uint256 hash, std::vector<unsigned char>& vchSig)
|
||||
{
|
||||
bool fOk = false;
|
||||
ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
|
||||
if (sig==NULL)
|
||||
return false;
|
||||
vchSig.clear();
|
||||
vchSig.resize(65,0);
|
||||
const BIGNUM *sig_r, *sig_s;
|
||||
ECDSA_SIG_get0(sig, &sig_r, &sig_s);
|
||||
int nBitsR = BN_num_bits(sig_r);
|
||||
int nBitsS = BN_num_bits(sig_s);
|
||||
if (nBitsR <= 256 && nBitsS <= 256)
|
||||
if (!fSet || !fHavePrivKey) return false;
|
||||
|
||||
vchSig.resize(65, 0);
|
||||
if (!ECDSA_sign_compact_secp256k1(&vchSig[0],
|
||||
reinterpret_cast<const unsigned char*>(&hash),
|
||||
vch,
|
||||
fCompressedPubKey))
|
||||
{
|
||||
int nRecId = -1;
|
||||
for (int i=0; i<4; i++)
|
||||
{
|
||||
CKey keyRec;
|
||||
keyRec.fSet = true;
|
||||
if (fCompressedPubKey)
|
||||
keyRec.SetCompressedPubKey();
|
||||
if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1)
|
||||
if (keyRec.GetPubKey() == this->GetPubKey())
|
||||
{
|
||||
nRecId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nRecId == -1)
|
||||
{
|
||||
ECDSA_SIG_free(sig);
|
||||
throw key_error("CKey::SignCompact() : unable to construct recoverable key");
|
||||
}
|
||||
|
||||
vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0);
|
||||
BN_bn2bin(sig_r,&vchSig[33-(nBitsR+7)/8]);
|
||||
BN_bn2bin(sig_s,&vchSig[65-(nBitsS+7)/8]);
|
||||
fOk = true;
|
||||
vchSig.clear();
|
||||
return false;
|
||||
}
|
||||
ECDSA_SIG_free(sig);
|
||||
return fOk;
|
||||
return true;
|
||||
}
|
||||
|
||||
// reconstruct public key from a compact signature
|
||||
// This is only slightly more CPU intensive than just verifying it.
|
||||
// If this function succeeds, the recovered public key is guaranteed to be valid
|
||||
// (the signature is a valid signature of the given data for that key)
|
||||
bool CKey::SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig)
|
||||
{
|
||||
if (vchSig.size() != 65)
|
||||
return false;
|
||||
if (vchSig.size() != 65) return false;
|
||||
int nV = vchSig[0];
|
||||
if (nV<27 || nV>=35)
|
||||
return false;
|
||||
ECDSA_SIG *sig = ECDSA_SIG_new();
|
||||
BIGNUM *sig_r = BN_bin2bn(&vchSig[1],32,NULL);
|
||||
BIGNUM *sig_s = BN_bin2bn(&vchSig[33],32,NULL);
|
||||
ECDSA_SIG_set0(sig, sig_r, sig_s);
|
||||
if (nV < 27 || nV >= 35) return false;
|
||||
|
||||
EC_KEY_free(pkey);
|
||||
pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
if (nV >= 31)
|
||||
{
|
||||
SetCompressedPubKey();
|
||||
nV -= 4;
|
||||
}
|
||||
if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), nV - 27, 0) == 1)
|
||||
{
|
||||
fSet = true;
|
||||
ECDSA_SIG_free(sig);
|
||||
return true;
|
||||
}
|
||||
ECDSA_SIG_free(sig);
|
||||
return false;
|
||||
unsigned char pubkey[65];
|
||||
std::size_t pubkey_len = 0;
|
||||
if (!ECDSA_recover_compact_secp256k1(pubkey, &pubkey_len,
|
||||
reinterpret_cast<const unsigned char*>(&hash),
|
||||
&vchSig[0]))
|
||||
return false;
|
||||
|
||||
std::vector<unsigned char> vchPub(pubkey, pubkey + pubkey_len);
|
||||
return SetPubKey(CPubKey(vchPub));
|
||||
}
|
||||
|
||||
bool CKey::Verify(uint256 hash, const std::vector<unsigned char>& vchSig)
|
||||
{
|
||||
// -1 = error, 0 = bad sig, 1 = good
|
||||
if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1)
|
||||
return false;
|
||||
if (vchSig.empty() || !fSet) return false;
|
||||
|
||||
return true;
|
||||
return ECDSA_verify_secp256k1(
|
||||
reinterpret_cast<const unsigned char*>(&hash),
|
||||
&vchSig[0], vchSig.size(),
|
||||
&vchPubKey[0], vchPubKey.size());
|
||||
}
|
||||
|
||||
bool CKey::VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig)
|
||||
{
|
||||
CKey key;
|
||||
if (!key.SetCompactSignature(hash, vchSig))
|
||||
return false;
|
||||
if (GetPubKey() != key.GetPubKey())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
if (!key.SetCompactSignature(hash, vchSig)) return false;
|
||||
return GetPubKey() == key.GetPubKey();
|
||||
}
|
||||
|
||||
bool CKey::IsValid()
|
||||
{
|
||||
if (!fSet)
|
||||
return false;
|
||||
if (!fSet) return false;
|
||||
|
||||
if (!EC_KEY_check_key(pkey))
|
||||
return false;
|
||||
if (fHavePrivKey) {
|
||||
if (!ECDSA_seckey_verify_secp256k1(vch)) return false;
|
||||
|
||||
bool fCompr;
|
||||
CSecret secret = GetSecret(fCompr);
|
||||
CKey key2;
|
||||
key2.SetSecret(secret, fCompr);
|
||||
return GetPubKey() == key2.GetPubKey();
|
||||
// Re-derive the pubkey and check it matches the cache. This is the
|
||||
// libsecp256k1 equivalent of OpenSSL's "consistency between priv and
|
||||
// pub" check the original implementation performed.
|
||||
unsigned char rederived[65];
|
||||
std::size_t rederived_len = 0;
|
||||
if (!ECDSA_pubkey_from_privkey_secp256k1(rederived, &rederived_len, vch, fCompressedPubKey))
|
||||
return false;
|
||||
if (rederived_len != vchPubKey.size()) return false;
|
||||
return std::memcmp(rederived, &vchPubKey[0], rederived_len) == 0;
|
||||
}
|
||||
|
||||
return ECDSA_pubkey_verify_secp256k1(&vchPubKey[0], vchPubKey.size());
|
||||
}
|
||||
|
||||
bool ECC_InitSanityCheck() {
|
||||
EC_KEY *pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
if(pkey == NULL)
|
||||
return false;
|
||||
EC_KEY_free(pkey);
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Startup smoke test for the cryptography backend.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
// TODO Is there more EC functionality that could be missing?
|
||||
bool ECC_InitSanityCheck()
|
||||
{
|
||||
// Verify that libsecp256k1 can validate a trivially-known good secret
|
||||
// (the scalar 1) and reject zero. If either of these fails, the linked
|
||||
// library is broken and we should refuse to start.
|
||||
static const unsigned char one[32] = {
|
||||
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
|
||||
};
|
||||
static const unsigned char zero[32] = {0};
|
||||
if (!ECDSA_seckey_verify_secp256k1(one)) return false;
|
||||
if ( ECDSA_seckey_verify_secp256k1(zero)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <openssl/ec.h> // for EC_KEY definition
|
||||
|
||||
// secp160k1
|
||||
// const unsigned int PRIVATE_KEY_SIZE = 192;
|
||||
// const unsigned int PUBLIC_KEY_SIZE = 41;
|
||||
@@ -70,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(
|
||||
@@ -101,24 +99,25 @@ 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 OpenSSL Elliptic Curve key (public and/or private) */
|
||||
/** An encapsulated secp256k1 elliptic-curve key (public and/or private). */
|
||||
class CKey
|
||||
{
|
||||
protected:
|
||||
EC_KEY* pkey;
|
||||
// 32-byte private scalar. Valid iff fSet && fHavePrivKey.
|
||||
unsigned char vch[32];
|
||||
// Cached serialized public key (33 or 65 bytes). Valid iff fSet.
|
||||
std::vector<unsigned char> vchPubKey;
|
||||
bool fSet;
|
||||
bool fCompressedPubKey;
|
||||
bool fHavePrivKey;
|
||||
|
||||
public:
|
||||
void SetCompressedPubKey();
|
||||
void SetUnCompressedPubKey();
|
||||
|
||||
EC_KEY* GetECKey();
|
||||
|
||||
|
||||
void Reset();
|
||||
|
||||
CKey();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#define TRIANGLES_KEYSTORE_H
|
||||
|
||||
#include "crypter.h"
|
||||
#include "util_signal.h"
|
||||
#include "sync.h"
|
||||
#include <boost/signals2/signal.hpp>
|
||||
|
||||
class CScript;
|
||||
|
||||
@@ -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,24 +157,23 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wallet status (encrypted, locked) changed.
|
||||
* Note: Called without locks held.
|
||||
*/
|
||||
boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
|
||||
CSignal<void(CCryptoKeyStore*)> NotifyStatusChanged;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -136,7 +136,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
|
||||
int64_t nFees = 0;
|
||||
{
|
||||
LOCK2(cs_main, mempool.cs);
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
|
||||
// Priority order to process transactions
|
||||
list<COrphan> vOrphan; // list memory doesn't move
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -387,6 +386,7 @@ void StakeMiner(CWallet *pwallet)
|
||||
RenameThread("Triangles-miner");
|
||||
|
||||
bool fTryToSync = true;
|
||||
bool fForceStaking = GetBoolArg("-forcestaking", false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -401,7 +401,7 @@ void StakeMiner(CWallet *pwallet)
|
||||
return;
|
||||
}
|
||||
|
||||
while (vNodes.empty() || IsInitialBlockDownload())
|
||||
while (!fForceStaking && (vNodes.empty() || IsInitialBlockDownload()))
|
||||
{
|
||||
nLastCoinStakeSearchInterval = 0;
|
||||
fTryToSync = true;
|
||||
@@ -410,7 +410,7 @@ void StakeMiner(CWallet *pwallet)
|
||||
return;
|
||||
}
|
||||
|
||||
if (fTryToSync)
|
||||
if (fTryToSync && !fForceStaking)
|
||||
{
|
||||
fTryToSync = false;
|
||||
if (vNodes.size() < 2 || nBestHeight < GetNumBlocksOfPeers())
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "irc.h"
|
||||
#include "db.h"
|
||||
#include "net.h"
|
||||
#include "main.h"
|
||||
@@ -48,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 {
|
||||
@@ -60,7 +59,7 @@ struct LocalServiceInfo {
|
||||
// Global state variables
|
||||
//
|
||||
bool fClient = false;
|
||||
//bool fDiscover = true;
|
||||
|
||||
|
||||
#ifdef USE_UPNP
|
||||
bool fUseUPnP = GetBoolArg("-upnp", USE_UPNP);
|
||||
@@ -72,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;
|
||||
|
||||
@@ -92,7 +91,7 @@ CCriticalSection cs_vOneShots;
|
||||
set<CNetAddr> setservAddNodeAddresses;
|
||||
CCriticalSection cs_setservAddNodeAddresses;
|
||||
|
||||
static CSemaphore *semOutbound = NULL;
|
||||
static CSemaphore *semOutbound = nullptr;
|
||||
|
||||
void AddOneShot(string strDest)
|
||||
{
|
||||
@@ -348,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)
|
||||
{
|
||||
@@ -424,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))
|
||||
@@ -470,7 +469,7 @@ CNode* FindNode(const CNetAddr& ip)
|
||||
if ((CNetAddr)pnode->addr == ip)
|
||||
return (pnode);
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CNode* FindNode(std::string addrName)
|
||||
@@ -479,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)
|
||||
@@ -490,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)
|
||||
@@ -500,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);
|
||||
@@ -558,7 +557,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -905,13 +904,9 @@ void ThreadSocketHandler2(void* parg)
|
||||
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
|
||||
if (lockRecv)
|
||||
{
|
||||
TRY_LOCK(pnode->cs_mapRequests, lockReq);
|
||||
if (lockReq)
|
||||
{
|
||||
TRY_LOCK(pnode->cs_inventory, lockInv);
|
||||
if (lockInv)
|
||||
fDelete = true;
|
||||
}
|
||||
TRY_LOCK(pnode->cs_inventory, lockInv);
|
||||
if (lockInv)
|
||||
fDelete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1047,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;
|
||||
@@ -1209,7 +1204,7 @@ void ThreadMapPort(void* parg)
|
||||
PrintException(&e, "ThreadMapPort()");
|
||||
} catch (...) {
|
||||
vnThreadsRunning[THREAD_UPNP]--;
|
||||
PrintException(NULL, "ThreadMapPort()");
|
||||
PrintException(nullptr, "ThreadMapPort()");
|
||||
}
|
||||
printf("ThreadMapPort exited\n");
|
||||
}
|
||||
@@ -1330,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");
|
||||
}
|
||||
}
|
||||
@@ -1408,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");
|
||||
@@ -1446,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");
|
||||
@@ -1504,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);
|
||||
@@ -1592,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 {
|
||||
@@ -1616,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) {
|
||||
@@ -1682,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()) {
|
||||
@@ -1790,7 +1785,7 @@ void ThreadHTTPSeedFetch(void* parg)
|
||||
PrintException(&e, "ThreadHTTPSeedFetch()");
|
||||
} catch (...) {
|
||||
vnThreadsRunning[THREAD_HTTPSEED]--;
|
||||
PrintException(NULL, "ThreadHTTPSeedFetch()");
|
||||
PrintException(nullptr, "ThreadHTTPSeedFetch()");
|
||||
}
|
||||
printf("ThreadHTTPSeedFetch exited\n");
|
||||
}
|
||||
@@ -1811,7 +1806,7 @@ void ThreadOpenConnections(void* parg)
|
||||
PrintException(&e, "ThreadOpenConnections()");
|
||||
} catch (...) {
|
||||
vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
|
||||
PrintException(NULL, "ThreadOpenConnections()");
|
||||
PrintException(nullptr, "ThreadOpenConnections()");
|
||||
}
|
||||
printf("ThreadOpenConnections exited\n");
|
||||
}
|
||||
@@ -1885,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);
|
||||
@@ -1993,7 +1988,7 @@ void ThreadOpenAddedConnections(void* parg)
|
||||
PrintException(&e, "ThreadOpenAddedConnections()");
|
||||
} catch (...) {
|
||||
vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--;
|
||||
PrintException(NULL, "ThreadOpenAddedConnections()");
|
||||
PrintException(nullptr, "ThreadOpenAddedConnections()");
|
||||
}
|
||||
printf("ThreadOpenAddedConnections exited\n");
|
||||
}
|
||||
@@ -2125,7 +2120,7 @@ void ThreadMessageHandler(void* parg)
|
||||
PrintException(&e, "ThreadMessageHandler()");
|
||||
} catch (...) {
|
||||
vnThreadsRunning[THREAD_MESSAGEHANDLER]--;
|
||||
PrintException(NULL, "ThreadMessageHandler()");
|
||||
PrintException(nullptr, "ThreadMessageHandler()");
|
||||
}
|
||||
printf("ThreadMessageHandler exited\n");
|
||||
}
|
||||
@@ -2146,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)
|
||||
@@ -2391,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));
|
||||
@@ -2400,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",
|
||||
@@ -2416,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)
|
||||
@@ -2429,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");
|
||||
}
|
||||
|
||||
@@ -2572,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);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#define TRIANGLES_NET_H
|
||||
|
||||
#include <deque>
|
||||
#include <boost/array.hpp>
|
||||
#include <array>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#ifndef WIN32
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "protocol.h"
|
||||
#include "addrman.h"
|
||||
|
||||
class CRequestTracker;
|
||||
class CNode;
|
||||
class CBlockIndex;
|
||||
bool IsInitialBlockDownload();
|
||||
@@ -35,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()));
|
||||
@@ -64,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
|
||||
@@ -76,25 +75,6 @@ enum
|
||||
MSG_BLOCK,
|
||||
};
|
||||
|
||||
class CRequestTracker
|
||||
{
|
||||
public:
|
||||
void (*fn)(void*, CDataStream&);
|
||||
void* param1;
|
||||
|
||||
explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL)
|
||||
{
|
||||
fn = fnIn;
|
||||
param1 = param1In;
|
||||
}
|
||||
|
||||
bool IsNull()
|
||||
{
|
||||
return fn == NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Thread types */
|
||||
enum threadId
|
||||
{
|
||||
@@ -118,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;
|
||||
@@ -268,8 +248,6 @@ protected:
|
||||
int nMisbehavior;
|
||||
|
||||
public:
|
||||
std::map<uint256, CRequestTracker> mapRequests;
|
||||
CCriticalSection cs_mapRequests;
|
||||
uint256 hashContinue;
|
||||
CBlockIndex* pindexLastGetBlocksBegin;
|
||||
uint256 hashLastGetBlocksEnd;
|
||||
@@ -466,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});
|
||||
}
|
||||
|
||||
|
||||
@@ -545,13 +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;
|
||||
using swallow = int[];
|
||||
(void)swallow{0, ((void)(ssSend << args), 0)...};
|
||||
EndMessage();
|
||||
}
|
||||
catch (...)
|
||||
@@ -561,180 +541,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
EndMessage();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
AbortMessage();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PushRequest(const char* pszCommand,
|
||||
void (*fn)(void*, CDataStream&), void* param1)
|
||||
{
|
||||
uint256 hashReply;
|
||||
RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
|
||||
|
||||
{
|
||||
LOCK(cs_mapRequests);
|
||||
mapRequests[hashReply] = CRequestTracker(fn, param1);
|
||||
}
|
||||
|
||||
PushMessage(pszCommand, hashReply);
|
||||
}
|
||||
|
||||
template<typename T1>
|
||||
void PushRequest(const char* pszCommand, const T1& a1,
|
||||
void (*fn)(void*, CDataStream&), void* param1)
|
||||
{
|
||||
uint256 hashReply;
|
||||
RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
|
||||
|
||||
{
|
||||
LOCK(cs_mapRequests);
|
||||
mapRequests[hashReply] = CRequestTracker(fn, param1);
|
||||
}
|
||||
|
||||
PushMessage(pszCommand, hashReply, a1);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,
|
||||
void (*fn)(void*, CDataStream&), void* param1)
|
||||
{
|
||||
uint256 hashReply;
|
||||
RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
|
||||
|
||||
{
|
||||
LOCK(cs_mapRequests);
|
||||
mapRequests[hashReply] = CRequestTracker(fn, param1);
|
||||
}
|
||||
|
||||
PushMessage(pszCommand, hashReply, a1, a2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd);
|
||||
void PushGetHeaders(CBlockIndex* pindexBegin, uint256 hashEnd);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/condition_variable.hpp>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
/**
|
||||
* Thread-safe notification queue for SSE (Server-Sent Events) clients.
|
||||
@@ -24,8 +25,8 @@
|
||||
class CNotificationQueue
|
||||
{
|
||||
private:
|
||||
mutable boost::mutex cs;
|
||||
boost::condition_variable cond;
|
||||
mutable std::mutex cs;
|
||||
std::condition_variable cond;
|
||||
|
||||
struct Event {
|
||||
uint64_t id;
|
||||
@@ -43,7 +44,7 @@ public:
|
||||
/** Push a new event. Wakes all waiting SSE clients. */
|
||||
void Push(const std::string& strData)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(cs);
|
||||
std::unique_lock<std::mutex> lock(cs);
|
||||
events.push_back(Event{nNextId++, strData});
|
||||
while (events.size() > MAX_QUEUED_EVENTS)
|
||||
events.pop_front();
|
||||
@@ -59,7 +60,7 @@ public:
|
||||
bool WaitForEvents(uint64_t& nLastId, std::vector<std::string>& vEvents, int nTimeoutMs, const volatile bool& fShutdown)
|
||||
{
|
||||
vEvents.clear();
|
||||
boost::mutex::scoped_lock lock(cs);
|
||||
std::unique_lock<std::mutex> lock(cs);
|
||||
|
||||
// Check for events already in the queue past our read position
|
||||
bool fHasNew = false;
|
||||
@@ -75,7 +76,7 @@ public:
|
||||
if (!fHasNew)
|
||||
{
|
||||
// Wait for new events or timeout
|
||||
cond.timed_wait(lock, boost::posix_time::milliseconds(nTimeoutMs));
|
||||
cond.wait_for(lock, std::chrono::milliseconds(nTimeoutMs));
|
||||
}
|
||||
|
||||
// Drain all events newer than nLastId
|
||||
@@ -97,7 +98,7 @@ public:
|
||||
/** Get the current latest event ID (for clients that want to skip history). */
|
||||
uint64_t GetLatestId() const
|
||||
{
|
||||
boost::mutex::scoped_lock lock(cs);
|
||||
std::unique_lock<std::mutex> lock(cs);
|
||||
return nNextId - 1;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
|
||||
#ifndef TRIANGLES_ONIONSEED_H
|
||||
#define TRIANGLES_ONIONSEED_H
|
||||
|
||||
// Hardcoded onion seed nodes for initial peer discovery.
|
||||
// Also fetched dynamically via https://seeds.cryptographic-triangles.org/seeds.txt
|
||||
static const char *strMainNetOnionSeed[][1] = {
|
||||
{"6ygpphp2qsucwvhwefv6h6ehvk6zjf7b7zdp4ggkzjjwe76cg6jwm7id.onion"},
|
||||
{"jbpfhe7zw3qm67wy3j2ayysp3mnrjobopthnko3b3sgahqtecblwqmid.onion"},
|
||||
{"uddaxjbo3lh2zskg7w6gwln4ty5cel7q4c5jbx7fdtv6zf2j47gdlyad.onion"},
|
||||
{"el5sirhhleecuctpeeprelzubpqmoqivvra3rzlwbjttinxa4fq3wnid.onion"},
|
||||
{"sj5dhybnlp3v4y5niyc5unrnd6s43lyx5ibup7rolyosjbi2u2hsbvyd.onion"},
|
||||
{"i3kr5meha7se4ns3wss3h7v46m6uksfzv4wrohdqxpj6n35wyo2bvlid.onion"},
|
||||
{NULL}
|
||||
// DNS2 - primary bootstrap server (194.233.88.206)
|
||||
{"gxvrhv3qitnc6kobrhsrse46bmcfitnybapor3or3oczzuxn6hfzxyid.onion"},
|
||||
// DNS3 - canonical chain reference (74.208.167.19)
|
||||
{"i6tk7soznftvoibtskwlezviskiererhjndpsmrff4kaxw7jnd5izfqd.onion"},
|
||||
// Hetzner Helsinki - ARM64 staking node (46.62.249.20)
|
||||
{"nawqqoazk2hhaglygulpeg6kh7hsgnvi2fursdvpvkantu4ojj26taid.onion"},
|
||||
// Contabo seed 1 (173.212.201.200)
|
||||
{"vmepp7plxngv4qpyngbgtb6njwnmlwy4api64xnwkhaf6fm3qlqtpfad.onion"},
|
||||
// Contabo seed 2
|
||||
{"nsldmfujkiwsfha42ajp5zx7gz3ekwdk4nvowdpf56mayuxnzshuykqd.onion"},
|
||||
// Contabo seed 3
|
||||
{"on4noksywc7b6cdbbxsp535l7j4cugunvlyz3iyhf6sfcg2qzaoy3eqd.onion"},
|
||||
// Contabo seed 4
|
||||
{"3uyzltm5cy7xzunncp3d7ariw75erabdnj4l3cxwvsxb6h4orc7eiqad.onion"},
|
||||
{nullptr}
|
||||
};
|
||||
|
||||
static const char *strTestNetOnionSeed[][1] = {
|
||||
{NULL}
|
||||
{nullptr}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "addresstablemodel.h"
|
||||
#include "transactiontablemodel.h"
|
||||
|
||||
#include "alert.h"
|
||||
#include "main.h"
|
||||
#include "ui_interface.h"
|
||||
|
||||
@@ -94,25 +93,6 @@ void ClientModel::updateNumConnections(int numConnections)
|
||||
emit numConnectionsChanged(numConnections);
|
||||
}
|
||||
|
||||
void ClientModel::updateAlert(const QString &hash, int status)
|
||||
{
|
||||
// Show error message notification for new alert
|
||||
if(status == CT_NEW)
|
||||
{
|
||||
uint256 hash_256;
|
||||
hash_256.SetHex(hash.toStdString());
|
||||
CAlert alert = CAlert::getAlertByHash(hash_256);
|
||||
if(!alert.IsNull())
|
||||
{
|
||||
emit error(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit a numBlocksChanged when the status message changes,
|
||||
// so that the view recomputes and updates the status bar.
|
||||
emit numBlocksChanged(getNumBlocks(), getNumBlocksOfPeers());
|
||||
}
|
||||
|
||||
double ClientModel::GetDifficulty() const
|
||||
{
|
||||
// Floating point number that is a multiple of the minimum difficulty,
|
||||
@@ -200,27 +180,15 @@ static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConn
|
||||
Q_ARG(int, newNumConnections));
|
||||
}
|
||||
|
||||
static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status)
|
||||
{
|
||||
if (fShutdown) return;
|
||||
OutputDebugStringF("NotifyAlertChanged %s status=%i\n", hash.GetHex().c_str(), status);
|
||||
QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
|
||||
Q_ARG(QString, QString::fromStdString(hash.GetHex())),
|
||||
Q_ARG(int, status));
|
||||
}
|
||||
|
||||
void ClientModel::subscribeToCoreSignals()
|
||||
{
|
||||
// Connect signals to client
|
||||
uiInterface.NotifyBlocksChanged.connect(boost::bind(NotifyBlocksChanged, this));
|
||||
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
||||
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
||||
m_core_signal_connections.add(uiInterface.NotifyBlocksChanged.connect(
|
||||
[this]() { NotifyBlocksChanged(this); }));
|
||||
m_core_signal_connections.add(uiInterface.NotifyNumConnectionsChanged.connect(
|
||||
[this](int n) { NotifyNumConnectionsChanged(this, n); }));
|
||||
}
|
||||
|
||||
void ClientModel::unsubscribeFromCoreSignals()
|
||||
{
|
||||
// Disconnect signals from client
|
||||
uiInterface.NotifyBlocksChanged.disconnect(boost::bind(NotifyBlocksChanged, this));
|
||||
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
|
||||
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
|
||||
m_core_signal_connections.disconnect_all();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "../util_signal.h"
|
||||
|
||||
class OptionsModel;
|
||||
class AddressTableModel;
|
||||
class TransactionTableModel;
|
||||
@@ -57,6 +59,8 @@ private:
|
||||
|
||||
void subscribeToCoreSignals();
|
||||
void unsubscribeFromCoreSignals();
|
||||
|
||||
CSignalConnections m_core_signal_connections;
|
||||
signals:
|
||||
void numConnectionsChanged(int count);
|
||||
void numBlocksChanged(int count, int countOfPeers);
|
||||
@@ -67,7 +71,6 @@ signals:
|
||||
public slots:
|
||||
void updateTimer();
|
||||
void updateNumConnections(int numConnections);
|
||||
void updateAlert(const QString &hash, int status);
|
||||
};
|
||||
|
||||
#endif // CLIENTMODEL_H
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#include <QDesktopServices>
|
||||
#include <QThread>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32_WINNT
|
||||
@@ -240,10 +240,10 @@ bool isObscured(QWidget *w)
|
||||
|
||||
void openDebugLogfile()
|
||||
{
|
||||
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
|
||||
std::filesystem::path pathDebug = GetDataDir() / "debug.log";
|
||||
|
||||
/* Open debug.log with the associated application */
|
||||
if (boost::filesystem::exists(pathDebug))
|
||||
if (std::filesystem::exists(pathDebug))
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathDebug.string())));
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
boost::filesystem::path static StartupShortcutPath()
|
||||
std::filesystem::path static StartupShortcutPath()
|
||||
{
|
||||
return GetSpecialFolderPath(CSIDL_STARTUP) / "triangles.lnk";
|
||||
}
|
||||
@@ -280,13 +280,13 @@ boost::filesystem::path static StartupShortcutPath()
|
||||
bool GetStartOnSystemStartup()
|
||||
{
|
||||
// check for triangles.lnk
|
||||
return boost::filesystem::exists(StartupShortcutPath());
|
||||
return std::filesystem::exists(StartupShortcutPath());
|
||||
}
|
||||
|
||||
bool SetStartOnSystemStartup(bool fAutoStart)
|
||||
{
|
||||
// If the shortcut exists already, remove it for updating
|
||||
boost::filesystem::remove(StartupShortcutPath());
|
||||
std::filesystem::remove(StartupShortcutPath());
|
||||
|
||||
if (fAutoStart)
|
||||
{
|
||||
@@ -343,9 +343,9 @@ bool SetStartOnSystemStartup(bool fAutoStart)
|
||||
// Follow the Desktop Application Autostart Spec:
|
||||
// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
|
||||
|
||||
boost::filesystem::path static GetAutostartDir()
|
||||
std::filesystem::path static GetAutostartDir()
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
char* pszConfigHome = getenv("XDG_CONFIG_HOME");
|
||||
if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
|
||||
@@ -354,14 +354,14 @@ boost::filesystem::path static GetAutostartDir()
|
||||
return fs::path();
|
||||
}
|
||||
|
||||
boost::filesystem::path static GetAutostartFilePath()
|
||||
std::filesystem::path static GetAutostartFilePath()
|
||||
{
|
||||
return GetAutostartDir() / "triangles.desktop";
|
||||
}
|
||||
|
||||
bool GetStartOnSystemStartup()
|
||||
{
|
||||
boost::filesystem::ifstream optionFile(GetAutostartFilePath());
|
||||
std::ifstream optionFile(GetAutostartFilePath());
|
||||
if (!optionFile.good())
|
||||
return false;
|
||||
// Scan through file for "Hidden=true":
|
||||
@@ -381,7 +381,7 @@ bool GetStartOnSystemStartup()
|
||||
bool SetStartOnSystemStartup(bool fAutoStart)
|
||||
{
|
||||
if (!fAutoStart)
|
||||
boost::filesystem::remove(GetAutostartFilePath());
|
||||
std::filesystem::remove(GetAutostartFilePath());
|
||||
else
|
||||
{
|
||||
char pszExePath[MAX_PATH+1];
|
||||
@@ -389,9 +389,9 @@ bool SetStartOnSystemStartup(bool fAutoStart)
|
||||
if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
|
||||
return false;
|
||||
|
||||
boost::filesystem::create_directories(GetAutostartDir());
|
||||
std::filesystem::create_directories(GetAutostartDir());
|
||||
|
||||
boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
|
||||
std::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
|
||||
if (!optionFile.good())
|
||||
return false;
|
||||
// Write a triangles.desktop file to the autostart directory:
|
||||
@@ -407,15 +407,15 @@ bool SetStartOnSystemStartup(bool fAutoStart)
|
||||
}
|
||||
#elif defined(Q_OS_MAC) || defined(MAC_OSX) || defined(__APPLE__)
|
||||
|
||||
boost::filesystem::path static GetLaunchAgentsDir()
|
||||
std::filesystem::path static GetLaunchAgentsDir()
|
||||
{
|
||||
const QString homeDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||
if (homeDir.isEmpty())
|
||||
return boost::filesystem::path();
|
||||
return boost::filesystem::path(homeDir.toStdString()) / "Library" / "LaunchAgents";
|
||||
return std::filesystem::path();
|
||||
return std::filesystem::path(homeDir.toStdString()) / "Library" / "LaunchAgents";
|
||||
}
|
||||
|
||||
boost::filesystem::path static GetAutostartFilePath()
|
||||
std::filesystem::path static GetAutostartFilePath()
|
||||
{
|
||||
return GetLaunchAgentsDir() / "org.triangles.triangles-qt.plist";
|
||||
}
|
||||
@@ -441,7 +441,7 @@ static std::string PlistEscape(const std::string& value)
|
||||
|
||||
bool GetStartOnSystemStartup()
|
||||
{
|
||||
boost::filesystem::ifstream optionFile(GetAutostartFilePath());
|
||||
std::ifstream optionFile(GetAutostartFilePath());
|
||||
if (!optionFile.good())
|
||||
return false;
|
||||
|
||||
@@ -459,16 +459,16 @@ bool GetStartOnSystemStartup()
|
||||
bool SetStartOnSystemStartup(bool fAutoStart)
|
||||
{
|
||||
if (!fAutoStart)
|
||||
return !boost::filesystem::exists(GetAutostartFilePath()) || boost::filesystem::remove(GetAutostartFilePath());
|
||||
return !std::filesystem::exists(GetAutostartFilePath()) || std::filesystem::remove(GetAutostartFilePath());
|
||||
|
||||
const QString exePath = QApplication::applicationFilePath();
|
||||
if (exePath.isEmpty())
|
||||
return false;
|
||||
|
||||
const QString workingDir = QFileInfo(exePath).absolutePath();
|
||||
boost::filesystem::create_directories(GetLaunchAgentsDir());
|
||||
std::filesystem::create_directories(GetLaunchAgentsDir());
|
||||
|
||||
boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
|
||||
std::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
|
||||
if (!optionFile.good())
|
||||
return false;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <QCheckBox>
|
||||
#include <QApplication>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -152,28 +152,28 @@ void IntroDialog::on_defaultRadio_toggled(bool checked)
|
||||
void IntroDialog::updateFreeSpace()
|
||||
{
|
||||
QString path = getDataDirectory();
|
||||
boost::filesystem::path fsPath(path.toStdString());
|
||||
std::filesystem::path fsPath(path.toStdString());
|
||||
|
||||
// Walk up to find an existing parent
|
||||
try {
|
||||
while (!fsPath.empty() && !boost::filesystem::exists(fsPath))
|
||||
while (!fsPath.empty() && !std::filesystem::exists(fsPath))
|
||||
fsPath = fsPath.parent_path();
|
||||
|
||||
if (!fsPath.empty()) {
|
||||
boost::filesystem::space_info si = boost::filesystem::space(fsPath);
|
||||
std::filesystem::space_info si = std::filesystem::space(fsPath);
|
||||
double freeGB = (double)si.available / (1024.0 * 1024.0 * 1024.0);
|
||||
freeSpaceLabel->setText(tr("Free space: %1 GB").arg(QString::number(freeGB, 'f', 2)));
|
||||
} else {
|
||||
freeSpaceLabel->setText(tr("Cannot determine free space"));
|
||||
}
|
||||
} catch (const boost::filesystem::filesystem_error &) {
|
||||
} catch (const std::filesystem::filesystem_error &) {
|
||||
freeSpaceLabel->setText(tr("Cannot determine free space"));
|
||||
}
|
||||
}
|
||||
|
||||
bool IntroDialog::pickDataDirectory()
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
QSettings settings;
|
||||
// If -datadir was passed on the command line, skip the dialog entirely
|
||||
@@ -305,10 +305,10 @@ bool IntroDialog::pickDataDirectory()
|
||||
return true;
|
||||
}
|
||||
|
||||
static void copyDirectoryRecursive(const boost::filesystem::path& src,
|
||||
const boost::filesystem::path& dst)
|
||||
static void copyDirectoryRecursive(const std::filesystem::path& src,
|
||||
const std::filesystem::path& dst)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
fs::create_directories(dst);
|
||||
for (fs::directory_iterator it(src), end; it != end; ++it) {
|
||||
fs::path dstChild = dst / it->path().filename();
|
||||
@@ -322,7 +322,7 @@ static void copyDirectoryRecursive(const boost::filesystem::path& src,
|
||||
|
||||
bool IntroDialog::migrateDataDirectory(const QString& oldPath, const QString& newPath)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
fs::path srcDir(oldPath.toStdString());
|
||||
fs::path dstDir(newPath.toStdString());
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
QDateTime received_datetime;
|
||||
|
||||
std::string sPrefix("im");
|
||||
leveldb::Iterator* it = dbSmsg.pdb->NewIterator(leveldb::ReadOptions());
|
||||
rocksdb::Iterator* it = dbSmsg.pdb->NewIterator(rocksdb::ReadOptions());
|
||||
while (dbSmsg.NextSmesg(it, sPrefix, chKey, smsgStored))
|
||||
{
|
||||
uint32_t nPayload = smsgStored.vchMessage.size() - SMSG_HDR_LEN;
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
delete it;
|
||||
|
||||
sPrefix = "sm";
|
||||
it = dbSmsg.pdb->NewIterator(leveldb::ReadOptions());
|
||||
it = dbSmsg.pdb->NewIterator(rocksdb::ReadOptions());
|
||||
while (dbSmsg.NextSmesg(it, sPrefix, chKey, smsgStored))
|
||||
{
|
||||
uint32_t nPayload = smsgStored.vchMessage.size() - SMSG_HDR_LEN;
|
||||
@@ -620,20 +620,19 @@ void MessageModel::subscribeToCoreSignals()
|
||||
{
|
||||
qRegisterMetaType<SecMsgStored>("SecMsgStored");
|
||||
|
||||
// Connect signals
|
||||
NotifySecMsgInboxChanged.connect(boost::bind(NotifySecMsgInbox, this, _1));
|
||||
NotifySecMsgOutboxChanged.connect(boost::bind(NotifySecMsgOutbox, this, _1));
|
||||
NotifySecMsgWalletUnlocked.connect(boost::bind(NotifySecMsgWallet, this));
|
||||
|
||||
m_core_signal_connections.add(NotifySecMsgInboxChanged.connect(
|
||||
[this](SecMsgStored& hdr) { NotifySecMsgInbox(this, hdr); }));
|
||||
m_core_signal_connections.add(NotifySecMsgOutboxChanged.connect(
|
||||
[this](SecMsgStored& hdr) { NotifySecMsgOutbox(this, hdr); }));
|
||||
m_core_signal_connections.add(NotifySecMsgWalletUnlocked.connect(
|
||||
[this]() { NotifySecMsgWallet(this); }));
|
||||
|
||||
connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int)));
|
||||
}
|
||||
|
||||
void MessageModel::unsubscribeFromCoreSignals()
|
||||
{
|
||||
// Disconnect signals
|
||||
NotifySecMsgInboxChanged.disconnect(boost::bind(NotifySecMsgInbox, this, _1));
|
||||
NotifySecMsgOutboxChanged.disconnect(boost::bind(NotifySecMsgOutbox, this, _1));
|
||||
NotifySecMsgWalletUnlocked.disconnect(boost::bind(NotifySecMsgWallet, this));
|
||||
|
||||
m_core_signal_connections.disconnect_all();
|
||||
|
||||
disconnect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int)));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include "allocators.h" /* for SecureString */
|
||||
#include "../util_signal.h"
|
||||
#include "smessage.h"
|
||||
#include <map>
|
||||
#include <QSortFilterProxyModel>
|
||||
@@ -175,6 +176,8 @@ private:
|
||||
void subscribeToCoreSignals();
|
||||
void unsubscribeFromCoreSignals();
|
||||
|
||||
CSignalConnections m_core_signal_connections;
|
||||
|
||||
public slots:
|
||||
|
||||
/* Check for new messages */
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "init.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
@@ -374,7 +374,7 @@ void OptionsDialog::on_dataDirBrowseButton_clicked()
|
||||
|
||||
void OptionsDialog::updateDataDirFreeSpace()
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
QString path = dataDirPath->text();
|
||||
fs::path fsPath(path.toStdString());
|
||||
try {
|
||||
@@ -395,7 +395,7 @@ void OptionsDialog::updateDataDirFreeSpace()
|
||||
|
||||
quint64 OptionsDialog::calculateDirSize(const QString& path)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
quint64 totalSize = 0;
|
||||
try {
|
||||
for (fs::recursive_directory_iterator it(path.toStdString()), end; it != end; ++it) {
|
||||
@@ -411,7 +411,7 @@ bool OptionsDialog::handleDataDirChange()
|
||||
if (m_pendingDataDir.isEmpty() || m_pendingDataDir == m_currentDataDir)
|
||||
return false;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
fs::path destPath(m_pendingDataDir.toStdString());
|
||||
|
||||
// Check destination is writable
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 844 B |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 844 B |
|
Before Width: | Height: | Size: 795 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 17 KiB |
@@ -244,7 +244,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
|
||||
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
|
||||
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
|
||||
|
||||
CTxDB txdb("r"); // To fetch source txouts
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder; // To fetch source txouts
|
||||
|
||||
strHTML += "<br><b>" + tr("Inputs") + ":</b>";
|
||||
strHTML += "<ul>";
|
||||
|
||||
@@ -145,7 +145,7 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
|
||||
// ... then triangles.conf:
|
||||
if (!boost::filesystem::is_directory(GetDataDir(false)))
|
||||
if (!std::filesystem::is_directory(GetDataDir(false)))
|
||||
{
|
||||
// This message can not be translated, as translation is not initialized yet
|
||||
// (which not yet possible because lang=XX can be overridden in triangles.conf in the data directory)
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -545,18 +545,21 @@ static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet,
|
||||
|
||||
void WalletModel::subscribeToCoreSignals()
|
||||
{
|
||||
// Connect signals to wallet
|
||||
wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
|
||||
wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
|
||||
wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
|
||||
m_core_signal_connections.add(wallet->NotifyStatusChanged.connect(
|
||||
[this](CCryptoKeyStore* w) { NotifyKeyStoreStatusChanged(this, w); }));
|
||||
m_core_signal_connections.add(wallet->NotifyAddressBookChanged.connect(
|
||||
[this](CWallet* w, const CTxDestination& address, const std::string& label, bool isMine, ChangeType status) {
|
||||
NotifyAddressBookChanged(this, w, address, label, isMine, status);
|
||||
}));
|
||||
m_core_signal_connections.add(wallet->NotifyTransactionChanged.connect(
|
||||
[this](CWallet* w, const uint256& hash, ChangeType status) {
|
||||
NotifyTransactionChanged(this, w, hash, status);
|
||||
}));
|
||||
}
|
||||
|
||||
void WalletModel::unsubscribeFromCoreSignals()
|
||||
{
|
||||
// Disconnect signals from wallet
|
||||
wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
|
||||
wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
|
||||
wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
|
||||
m_core_signal_connections.disconnect_all();
|
||||
}
|
||||
|
||||
// WalletModel::UnlockContext implementation
|
||||
@@ -673,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;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QMutex>
|
||||
|
||||
#include "allocators.h" /* for SecureString */
|
||||
#include "../util_signal.h"
|
||||
|
||||
class OptionsModel;
|
||||
class AddressTableModel;
|
||||
@@ -92,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);
|
||||
@@ -102,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
|
||||
{
|
||||
@@ -157,6 +164,8 @@ private:
|
||||
void unsubscribeFromCoreSignals();
|
||||
bool checkBalanceChanged();
|
||||
|
||||
CSignalConnections m_core_signal_connections;
|
||||
|
||||
|
||||
public slots:
|
||||
/* Wallet status might have changed */
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -11,6 +11,19 @@
|
||||
#include "base58.h"
|
||||
#include "utxosnapshot.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
// Windows.h (transitively included) defines these as macros, clobbering Checkpoints:: enum values.
|
||||
#ifdef STRICT
|
||||
#undef STRICT
|
||||
#endif
|
||||
#ifdef ADVISORY
|
||||
#undef ADVISORY
|
||||
#endif
|
||||
#ifdef PERMISSIVE
|
||||
#undef PERMISSIVE
|
||||
#endif
|
||||
|
||||
using namespace json_spirit;
|
||||
using namespace std;
|
||||
|
||||
@@ -21,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;
|
||||
@@ -85,7 +98,7 @@ double GetPoSKernelPS()
|
||||
int nStakesHandled = 0, nStakesTime = 0;
|
||||
|
||||
CBlockIndex* pindex = pindexBest;;
|
||||
CBlockIndex* pindexPrevStake = NULL;
|
||||
CBlockIndex* pindexPrevStake = nullptr;
|
||||
|
||||
while (pindex && nStakesHandled < nPoSInterval)
|
||||
{
|
||||
@@ -115,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')));
|
||||
@@ -317,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')));
|
||||
@@ -382,7 +395,7 @@ static int64_t ComputeActiveChainSupplyFromBlocks(const std::vector<CBlockIndex*
|
||||
nBlocksScanned = 0;
|
||||
nTransactionsScanned = 0;
|
||||
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
int64_t nSupply = 0;
|
||||
|
||||
for (std::vector<CBlockIndex*>::const_iterator pindexIt = chain.begin(); pindexIt != chain.end(); ++pindexIt)
|
||||
@@ -458,7 +471,7 @@ Value recalculatesupply(const Array& params, bool fHelp)
|
||||
if (!pindexBest)
|
||||
throw runtime_error("recalculatesupply: no best block");
|
||||
|
||||
CTxDB txdbRead("r");
|
||||
auto txdbRead_holder = MakeChainDB("r"); CTxDBBase& txdbRead = *txdbRead_holder;
|
||||
int nUtxoCount = 0;
|
||||
int64_t nUtxoSupply = txdbRead.SumUtxoValues(nUtxoCount);
|
||||
|
||||
@@ -473,7 +486,7 @@ Value recalculatesupply(const Array& params, bool fHelp)
|
||||
|
||||
if (fApply)
|
||||
{
|
||||
CTxDB txdbWrite;
|
||||
auto txdbWrite_holder = MakeChainDB(); CTxDBBase& txdbWrite = *txdbWrite_holder;
|
||||
int64_t nRunningSupply = 0;
|
||||
|
||||
for (std::vector<CBlockIndex*>::const_iterator pindexIt = activeChain.begin(); pindexIt != activeChain.end(); ++pindexIt)
|
||||
@@ -545,6 +558,105 @@ Value recalculatesupply(const Array& params, bool fHelp)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Walks every non-coinbase transaction input across [start_height, end_height]
|
||||
// and runs the existing VerifySignature path. Reports counts and the first 100
|
||||
// failures so the caller can spot regressions when the underlying ECDSA
|
||||
// implementation changes (e.g. OpenSSL EC -> libsecp256k1).
|
||||
Value auditsignatures(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"auditsignatures [start_height] [end_height]\n"
|
||||
"Walk the active chain in [start_height, end_height] (inclusive) and run\n"
|
||||
"VerifySignature on every non-coinbase input. Returns counts plus up to 100\n"
|
||||
"failures.\n"
|
||||
"Defaults: start = max(1, tip-1000), end = tip.\n"
|
||||
"Pre-migration this should always report 0 failures; post-migration any non-zero\n"
|
||||
"result identifies a behavioural regression in the new ECDSA path.");
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (!pindexBest)
|
||||
throw runtime_error("auditsignatures: no best block");
|
||||
|
||||
int tip = nBestHeight;
|
||||
int start = (params.size() > 0) ? params[0].get_int() : std::max(1, tip - 1000);
|
||||
int end = (params.size() > 1) ? params[1].get_int() : tip;
|
||||
|
||||
if (start < 1) throw runtime_error("auditsignatures: start_height must be >= 1");
|
||||
if (end > tip) throw runtime_error("auditsignatures: end_height exceeds tip");
|
||||
if (start > end) throw runtime_error("auditsignatures: start_height > end_height");
|
||||
|
||||
// Build forward walk by descending from tip.
|
||||
CBlockIndex* pindex = pindexBest;
|
||||
while (pindex && pindex->nHeight > end)
|
||||
pindex = pindex->pprev;
|
||||
|
||||
std::vector<CBlockIndex*> walk;
|
||||
while (pindex && pindex->nHeight >= start) {
|
||||
walk.push_back(pindex);
|
||||
pindex = pindex->pprev;
|
||||
}
|
||||
std::reverse(walk.begin(), walk.end());
|
||||
|
||||
int nBlocksScanned = 0;
|
||||
int64_t nInputsChecked = 0;
|
||||
int64_t nInputsFailed = 0;
|
||||
Array failures;
|
||||
const size_t kMaxFailures = 100;
|
||||
|
||||
auto recordFailure = [&](int height, const uint256& txid, unsigned int vin, const char* reason) {
|
||||
++nInputsFailed;
|
||||
if (failures.size() >= kMaxFailures) return;
|
||||
Object f;
|
||||
f.push_back(Pair("height", height));
|
||||
f.push_back(Pair("txid", txid.GetHex()));
|
||||
f.push_back(Pair("vin", (int)vin));
|
||||
f.push_back(Pair("reason", reason));
|
||||
failures.push_back(f);
|
||||
};
|
||||
|
||||
for (CBlockIndex* pi : walk) {
|
||||
CBlock block;
|
||||
if (!block.ReadFromDisk(pi, true)) {
|
||||
++nBlocksScanned;
|
||||
continue;
|
||||
}
|
||||
for (const CTransaction& tx : block.vtx) {
|
||||
if (tx.IsCoinBase()) continue;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
||||
const COutPoint& prev = tx.vin[i].prevout;
|
||||
CTransaction txFrom;
|
||||
uint256 hashBlock;
|
||||
if (!GetTransaction(prev.hash, txFrom, hashBlock)) {
|
||||
recordFailure(pi->nHeight, tx.GetHash(), i, "prevout transaction not found");
|
||||
continue;
|
||||
}
|
||||
if (prev.n >= txFrom.vout.size()) {
|
||||
recordFailure(pi->nHeight, tx.GetHash(), i, "prevout index out of range");
|
||||
continue;
|
||||
}
|
||||
++nInputsChecked;
|
||||
if (!VerifySignature(txFrom, tx, i, 0))
|
||||
recordFailure(pi->nHeight, tx.GetHash(), i, "VerifySignature returned false");
|
||||
}
|
||||
}
|
||||
++nBlocksScanned;
|
||||
if (nBlocksScanned % 1000 == 0)
|
||||
printf("auditsignatures: scanned %d blocks, %lld inputs, %lld failures\n",
|
||||
nBlocksScanned, (long long)nInputsChecked, (long long)nInputsFailed);
|
||||
}
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("start_height", start));
|
||||
result.push_back(Pair("end_height", end));
|
||||
result.push_back(Pair("blocks_scanned", nBlocksScanned));
|
||||
result.push_back(Pair("inputs_checked", nInputsChecked));
|
||||
result.push_back(Pair("inputs_failed", nInputsFailed));
|
||||
result.push_back(Pair("failures", failures));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// triangles: get information of sync-checkpoint
|
||||
Value getcheckpoint(const Array& params, bool fHelp)
|
||||
@@ -596,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;
|
||||
@@ -706,7 +818,7 @@ Value getaddressbalance(const Array& params, bool fHelp)
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: " + strAddr);
|
||||
|
||||
int64_t nBalance = 0;
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
txdb.ReadAddressBalance(nType, hashBytes, nBalance);
|
||||
nTotalBalance += nBalance;
|
||||
}
|
||||
@@ -741,7 +853,7 @@ Value getaddressutxos(const Array& params, bool fHelp)
|
||||
Array addrArray = find_value(addrObj, "addresses").get_array();
|
||||
|
||||
Array result;
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
|
||||
for (unsigned int i = 0; i < addrArray.size(); i++)
|
||||
{
|
||||
@@ -801,7 +913,7 @@ Value getaddresstxids(const Array& params, bool fHelp)
|
||||
nEndHeight = endVal.get_int();
|
||||
|
||||
Array result;
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
|
||||
// Use a set to deduplicate txids across multiple addresses
|
||||
std::set<uint256> setTxIds;
|
||||
@@ -907,7 +1019,7 @@ Value invalidateblock(const Array& params, bool fHelp)
|
||||
|
||||
if (pindex->IsInMainChain())
|
||||
{
|
||||
CTxDB txdb;
|
||||
auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder;
|
||||
if (!txdb.TxnBegin())
|
||||
throw runtime_error("Failed to begin transaction.");
|
||||
|
||||
@@ -930,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;
|
||||
}
|
||||
|
||||
@@ -976,7 +1088,7 @@ Value reconsiderblock(const Array& params, bool fHelp)
|
||||
if (!block.ReadFromDisk(pindex))
|
||||
throw runtime_error("Failed to read block from disk.");
|
||||
|
||||
CTxDB txdb;
|
||||
auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder;
|
||||
block.SetBestChain(txdb, pindex);
|
||||
printf("reconsiderblock: reconsidered block %s at height %d, new best height=%d\n",
|
||||
hash.ToString().c_str(), pindex->nHeight, nBestHeight);
|
||||
@@ -1016,7 +1128,7 @@ Value dumputxoset(const Array& params, bool fHelp)
|
||||
if (nHeaders < 100)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "nheaders must be at least 100");
|
||||
|
||||
boost::filesystem::path destPath(filename);
|
||||
std::filesystem::path destPath(filename);
|
||||
std::string strError;
|
||||
|
||||
if (!UtxoSnapshot::DumpSnapshot(destPath, nHeaders, strError))
|
||||
@@ -1024,8 +1136,8 @@ Value dumputxoset(const Array& params, bool fHelp)
|
||||
|
||||
// Get file size
|
||||
int64_t nFileSize = 0;
|
||||
if (boost::filesystem::exists(destPath))
|
||||
nFileSize = (int64_t)boost::filesystem::file_size(destPath);
|
||||
if (std::filesystem::exists(destPath))
|
||||
nFileSize = (int64_t)std::filesystem::file_size(destPath);
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("filename", filename));
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "net.h"
|
||||
#include "addrman.h"
|
||||
#include "trianglesrpc.h"
|
||||
#include "alert.h"
|
||||
#include "wallet.h"
|
||||
#include "db.h"
|
||||
#include "walletdb.h"
|
||||
@@ -32,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;
|
||||
@@ -89,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));
|
||||
@@ -107,71 +106,6 @@ Value getpeerinfo(const Array& params, bool fHelp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern CCriticalSection cs_mapAlerts;
|
||||
extern map<uint256, CAlert> mapAlerts;
|
||||
|
||||
// triangles: send alert.
|
||||
// There is a known deadlock situation with ThreadMessageHandler
|
||||
// ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
|
||||
// ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
|
||||
Value sendalert(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 6)
|
||||
throw runtime_error(
|
||||
"sendalert <message> <privatekey> <minver> <maxver> <priority> <id> [cancelupto]\n"
|
||||
"<message> is the alert text message\n"
|
||||
"<privatekey> is hex string of alert master private key\n"
|
||||
"<minver> is the minimum applicable internal client version\n"
|
||||
"<maxver> is the maximum applicable internal client version\n"
|
||||
"<priority> is integer priority number\n"
|
||||
"<id> is the alert id\n"
|
||||
"[cancelupto] cancels all alert id's up to this number\n"
|
||||
"Returns true or false.");
|
||||
|
||||
CAlert alert;
|
||||
CKey key;
|
||||
|
||||
alert.strStatusBar = params[0].get_str();
|
||||
alert.nMinVer = params[2].get_int();
|
||||
alert.nMaxVer = params[3].get_int();
|
||||
alert.nPriority = params[4].get_int();
|
||||
alert.nID = params[5].get_int();
|
||||
if (params.size() > 6)
|
||||
alert.nCancel = params[6].get_int();
|
||||
alert.nVersion = PROTOCOL_VERSION;
|
||||
alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
|
||||
alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
|
||||
|
||||
CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
|
||||
sMsg << (CUnsignedAlert)alert;
|
||||
alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
|
||||
|
||||
vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
|
||||
key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
|
||||
if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
|
||||
throw runtime_error(
|
||||
"Unable to sign alert, check private key?\n");
|
||||
if(!alert.ProcessAlert())
|
||||
throw runtime_error(
|
||||
"Failed to process alert.\n");
|
||||
// Relay alert
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
for (CNode* pnode : vNodes)
|
||||
alert.RelayTo(pnode);
|
||||
}
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("strStatusBar", alert.strStatusBar));
|
||||
result.push_back(Pair("nVersion", alert.nVersion));
|
||||
result.push_back(Pair("nMinVer", alert.nMinVer));
|
||||
result.push_back(Pair("nMaxVer", alert.nMaxVer));
|
||||
result.push_back(Pair("nPriority", alert.nPriority));
|
||||
result.push_back(Pair("nID", alert.nID));
|
||||
if (alert.nCancel > 0)
|
||||
result.push_back(Pair("nCancel", alert.nCancel));
|
||||
return result;
|
||||
}
|
||||
|
||||
Value addnode(const Array& params, bool fHelp)
|
||||
{
|
||||
@@ -261,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);
|
||||
}
|
||||
|
||||
@@ -340,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;
|
||||
|
||||
@@ -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));
|
||||
@@ -371,7 +371,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
||||
CTransaction tempTx;
|
||||
MapPrevTx mapPrevTx;
|
||||
MapPrevTx mapEmpty;
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
bool fInvalid;
|
||||
|
||||
// FetchInputs aborts on failure, so we go one at a time.
|
||||
@@ -548,11 +548,11 @@ Value sendrawtransaction(const Array& params, bool fHelp)
|
||||
else
|
||||
{
|
||||
// push to local node
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
if (!tx.AcceptToMemoryPool(txdb))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
|
||||
|
||||
SyncWithWallets(tx, NULL, true);
|
||||
SyncWithWallets(tx, nullptr, true);
|
||||
}
|
||||
RelayTransaction(tx, hashTx);
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "smessage.h"
|
||||
#include "init.h" // pwalletMain
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
using namespace json_spirit;
|
||||
using namespace std;
|
||||
|
||||
@@ -607,7 +609,7 @@ Value smsginbox(const Array& params, bool fHelp)
|
||||
{
|
||||
dbInbox.TxnBegin();
|
||||
|
||||
leveldb::Iterator* it = dbInbox.pdb->NewIterator(leveldb::ReadOptions());
|
||||
rocksdb::Iterator* it = dbInbox.pdb->NewIterator(rocksdb::ReadOptions());
|
||||
while (dbInbox.NextSmesgKey(it, sPrefix, chKey))
|
||||
{
|
||||
dbInbox.EraseSmesg(chKey);
|
||||
@@ -629,7 +631,7 @@ Value smsginbox(const Array& params, bool fHelp)
|
||||
|
||||
dbInbox.TxnBegin();
|
||||
|
||||
leveldb::Iterator* it = dbInbox.pdb->NewIterator(leveldb::ReadOptions());
|
||||
rocksdb::Iterator* it = dbInbox.pdb->NewIterator(rocksdb::ReadOptions());
|
||||
while (dbInbox.NextSmesg(it, sPrefix, chKey, smsgStored))
|
||||
{
|
||||
if (fCheckReadStatus
|
||||
@@ -717,7 +719,7 @@ Value smsgoutbox(const Array& params, bool fHelp)
|
||||
{
|
||||
dbOutbox.TxnBegin();
|
||||
|
||||
leveldb::Iterator* it = dbOutbox.pdb->NewIterator(leveldb::ReadOptions());
|
||||
rocksdb::Iterator* it = dbOutbox.pdb->NewIterator(rocksdb::ReadOptions());
|
||||
while (dbOutbox.NextSmesgKey(it, sPrefix, chKey))
|
||||
{
|
||||
dbOutbox.EraseSmesg(chKey);
|
||||
@@ -734,7 +736,7 @@ Value smsgoutbox(const Array& params, bool fHelp)
|
||||
{
|
||||
SecMsgStored smsgStored;
|
||||
MessageData msg;
|
||||
leveldb::Iterator* it = dbOutbox.pdb->NewIterator(leveldb::ReadOptions());
|
||||
rocksdb::Iterator* it = dbOutbox.pdb->NewIterator(rocksdb::ReadOptions());
|
||||
while (dbOutbox.NextSmesg(it, sPrefix, chKey, smsgStored))
|
||||
{
|
||||
uint32_t nPayload = smsgStored.vchMessage.size() - SMSG_HDR_LEN;
|
||||
@@ -820,10 +822,10 @@ Value smsgbuckets(const Array& params, bool fHelp)
|
||||
objM.push_back(Pair("hash", sHash));
|
||||
objM.push_back(Pair("last changed", getTimeString(it->second.timeChanged, cbuf, sizeof(cbuf))));
|
||||
|
||||
boost::filesystem::path fullPath = GetDataDir() / "smsgStore" / sFile;
|
||||
std::filesystem::path fullPath = GetDataDir() / "smsgStore" / sFile;
|
||||
|
||||
|
||||
if (!boost::filesystem::exists(fullPath))
|
||||
if (!std::filesystem::exists(fullPath))
|
||||
{
|
||||
// -- If there is a file for an empty bucket something is wrong.
|
||||
if (tokenSet.size() == 0)
|
||||
@@ -835,10 +837,10 @@ Value smsgbuckets(const Array& params, bool fHelp)
|
||||
try {
|
||||
|
||||
uint64_t nFBytes = 0;
|
||||
nFBytes = boost::filesystem::file_size(fullPath);
|
||||
nFBytes = std::filesystem::file_size(fullPath);
|
||||
nBytes += nFBytes;
|
||||
objM.push_back(Pair("file size", fsReadable(nFBytes)));
|
||||
} catch (const boost::filesystem::filesystem_error& ex)
|
||||
} catch (const std::filesystem::filesystem_error& ex)
|
||||
{
|
||||
objM.push_back(Pair("file size, error", ex.what()));
|
||||
};
|
||||
@@ -871,9 +873,9 @@ Value smsgbuckets(const Array& params, bool fHelp)
|
||||
std::string sFile = std::to_string(it->first) + "_01.dat";
|
||||
|
||||
try {
|
||||
boost::filesystem::path fullPath = GetDataDir() / "smsgStore" / sFile;
|
||||
boost::filesystem::remove(fullPath);
|
||||
} catch (const boost::filesystem::filesystem_error& ex)
|
||||
std::filesystem::path fullPath = GetDataDir() / "smsgStore" / sFile;
|
||||
std::filesystem::remove(fullPath);
|
||||
} catch (const std::filesystem::filesystem_error& ex)
|
||||
{
|
||||
//objM.push_back(Pair("file size, error", ex.what()));
|
||||
printf("Error removing bucket file %s.\n", ex.what());
|
||||
@@ -930,7 +932,7 @@ Value smsgbroadcast(const Array& params, bool fHelp)
|
||||
|
||||
// Iterate all "pk" entries
|
||||
std::string sPrefix("pk");
|
||||
leveldb::Iterator* it = dbPub.pdb->NewIterator(leveldb::ReadOptions());
|
||||
rocksdb::Iterator* it = dbPub.pdb->NewIterator(rocksdb::ReadOptions());
|
||||
for (it->Seek(sPrefix); it->Valid(); it->Next())
|
||||
{
|
||||
std::string key = it->key().ToString();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1860,179 +1860,76 @@ return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Value clearwallettransactions(const Array& params, bool fHelp)
|
||||
Value hdinfo(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 0)
|
||||
throw runtime_error(
|
||||
"clearwallettransactions \n"
|
||||
"delete all transactions from wallet - reload with scanforalltxns\n"
|
||||
"Warning: Backup your wallet first!");
|
||||
|
||||
|
||||
|
||||
Object result;
|
||||
|
||||
uint32_t nTransactions = 0;
|
||||
|
||||
char cbuf[256];
|
||||
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
CWalletDB walletdb(pwalletMain->strWalletFile);
|
||||
walletdb.TxnBegin();
|
||||
Dbc* pcursor = walletdb.GetTxnCursor();
|
||||
if (!pcursor)
|
||||
throw runtime_error("Cannot get wallet DB cursor");
|
||||
|
||||
// RAII guard ensures cursor is closed even on exception
|
||||
struct CursorGuard {
|
||||
Dbc* cur;
|
||||
CursorGuard(Dbc* c) : cur(c) {}
|
||||
~CursorGuard() { if (cur) cur->close(); }
|
||||
} cursorGuard(pcursor);
|
||||
|
||||
Dbt datKey;
|
||||
Dbt datValue;
|
||||
|
||||
datKey.set_flags(DB_DBT_USERMEM);
|
||||
datValue.set_flags(DB_DBT_USERMEM);
|
||||
|
||||
std::vector<unsigned char> vchKey;
|
||||
std::vector<unsigned char> vchType;
|
||||
std::vector<unsigned char> vchKeyData;
|
||||
std::vector<unsigned char> vchValueData;
|
||||
|
||||
vchKeyData.resize(100);
|
||||
vchValueData.resize(100);
|
||||
|
||||
datKey.set_ulen(vchKeyData.size());
|
||||
datKey.set_data(&vchKeyData[0]);
|
||||
|
||||
datValue.set_ulen(vchValueData.size());
|
||||
datValue.set_data(&vchValueData[0]);
|
||||
|
||||
unsigned int fFlags = DB_NEXT; // same as using DB_FIRST for new cursor
|
||||
while (true)
|
||||
{
|
||||
int ret = pcursor->get(&datKey, &datValue, fFlags);
|
||||
|
||||
if (ret == ENOMEM
|
||||
|| ret == DB_BUFFER_SMALL)
|
||||
{
|
||||
if (datKey.get_size() > datKey.get_ulen())
|
||||
{
|
||||
vchKeyData.resize(datKey.get_size());
|
||||
datKey.set_ulen(vchKeyData.size());
|
||||
datKey.set_data(&vchKeyData[0]);
|
||||
};
|
||||
|
||||
if (datValue.get_size() > datValue.get_ulen())
|
||||
{
|
||||
vchValueData.resize(datValue.get_size());
|
||||
datValue.set_ulen(vchValueData.size());
|
||||
datValue.set_data(&vchValueData[0]);
|
||||
};
|
||||
// -- try once more, when DB_BUFFER_SMALL cursor is not expected to move
|
||||
ret = pcursor->get(&datKey, &datValue, fFlags);
|
||||
};
|
||||
|
||||
if (ret == DB_NOTFOUND)
|
||||
break;
|
||||
else
|
||||
if (datKey.get_data() == NULL || datValue.get_data() == NULL
|
||||
|| ret != 0)
|
||||
{
|
||||
const char* dbErr = db_strerror(ret);
|
||||
snprintf(cbuf, sizeof(cbuf), "wallet DB error %d, %s", ret, dbErr ? dbErr : "unknown");
|
||||
throw runtime_error(cbuf);
|
||||
};
|
||||
|
||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
||||
ssValue.SetType(SER_DISK);
|
||||
ssValue.clear();
|
||||
ssValue.write((char*)datKey.get_data(), datKey.get_size());
|
||||
|
||||
ssValue >> vchType;
|
||||
|
||||
|
||||
std::string strType(vchType.begin(), vchType.end());
|
||||
|
||||
//printf("strType %s\n", strType.c_str());
|
||||
|
||||
if (strType == "tx")
|
||||
{
|
||||
uint256 hash;
|
||||
ssValue >> hash;
|
||||
|
||||
if ((ret = pcursor->del(0)) != 0)
|
||||
{
|
||||
printf("Delete transaction failed %d, %s\n", ret, db_strerror(ret));
|
||||
continue;
|
||||
};
|
||||
|
||||
pwalletMain->mapWallet.erase(hash);
|
||||
try { pwalletMain->NotifyTransactionChanged(pwalletMain, hash, CT_DELETED); }
|
||||
catch (...) {
|
||||
printf("clearwallettransactions: NotifyTransactionChanged failed\n");
|
||||
}
|
||||
|
||||
nTransactions++;
|
||||
};
|
||||
};
|
||||
cursorGuard.cur = nullptr; // mark as handled
|
||||
pcursor->close();
|
||||
walletdb.TxnCommit();
|
||||
}
|
||||
|
||||
snprintf(cbuf, sizeof(cbuf), "Removed %u transactions.", nTransactions);
|
||||
result.push_back(Pair("complete", std::string(cbuf)));
|
||||
result.push_back(Pair("", "Reload with scanforstealthtxns or re-download blockchain."));
|
||||
|
||||
|
||||
return result;
|
||||
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 scanforalltxns(const Array& params, bool fHelp)
|
||||
Value hdnew(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"scanforalltxns [fromHeight]\n"
|
||||
"Scan blockchain for owned transactions.");
|
||||
|
||||
Object result;
|
||||
int32_t nFromHeight = 0;
|
||||
|
||||
CBlockIndex *pindex = pindexGenesisBlock;
|
||||
|
||||
|
||||
if (params.size() > 0)
|
||||
nFromHeight = params[0].get_int();
|
||||
|
||||
|
||||
if (nFromHeight > 0)
|
||||
{
|
||||
pindex = mapBlockIndex[hashBestChain];
|
||||
while (pindex->nHeight > nFromHeight
|
||||
&& pindex->pprev)
|
||||
pindex = pindex->pprev;
|
||||
};
|
||||
|
||||
if (pindex == NULL)
|
||||
throw runtime_error("Genesis Block is not set.");
|
||||
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
pwalletMain->MarkDirty();
|
||||
|
||||
pwalletMain->ScanForWalletTransactions(pindex, true);
|
||||
pwalletMain->ReacceptWalletTransactions();
|
||||
}
|
||||
|
||||
result.push_back(Pair("result", "Scan complete."));
|
||||
|
||||
return result;
|
||||
"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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <cassert>
|
||||
#include <ios>
|
||||
#include <limits>
|
||||
#include <stdint.h>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <boost/type_traits/is_fundamental.hpp>
|
||||
#include <type_traits>
|
||||
#include <tuple>
|
||||
|
||||
#include "allocators.h"
|
||||
@@ -58,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} \
|
||||
@@ -73,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} \
|
||||
}
|
||||
|
||||
@@ -287,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
|
||||
@@ -391,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)
|
||||
@@ -408,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())
|
||||
@@ -421,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)
|
||||
@@ -431,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();
|
||||
@@ -452,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);
|
||||
@@ -472,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>{});
|
||||
}
|
||||
|
||||
|
||||
@@ -704,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.
|
||||
*
|
||||
@@ -1059,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); }
|
||||
|
||||
|
||||
//
|
||||
|
||||
@@ -41,18 +41,15 @@ Notes:
|
||||
#include <errno.h>
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdh.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#include <string>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
||||
#include "base58.h"
|
||||
#include "crypto_ecdh.h"
|
||||
#include "db.h"
|
||||
#include "init.h" // pwalletMain
|
||||
#include "txdb.h"
|
||||
@@ -60,6 +57,12 @@ Notes:
|
||||
|
||||
#include "lz4/lz4.h"
|
||||
|
||||
// LevelDB headers retained solely for the one-shot smsgDB leveldb→rocksdb
|
||||
// migration in MigrateSmsgDBLevelDbToRocksDb. Once all users have upgraded
|
||||
// past v5.10 the migration helper (and these includes) can be dropped.
|
||||
#include <leveldb/db.h>
|
||||
#include <leveldb/iterator.h>
|
||||
|
||||
#include "xxhash/xxhash.h"
|
||||
#include "xxhash/xxhash.c"
|
||||
|
||||
@@ -77,9 +80,9 @@ Notes:
|
||||
|
||||
// TODO: For buckets older than current, only need to store no. messages and hash in memory
|
||||
|
||||
boost::signals2::signal<void (SecMsgStored& inboxHdr)> NotifySecMsgInboxChanged;
|
||||
boost::signals2::signal<void (SecMsgStored& outboxHdr)> NotifySecMsgOutboxChanged;
|
||||
boost::signals2::signal<void ()> NotifySecMsgWalletUnlocked;
|
||||
CSignal<void(SecMsgStored&)> NotifySecMsgInboxChanged;
|
||||
CSignal<void(SecMsgStored&)> NotifySecMsgOutboxChanged;
|
||||
CSignal<void()> NotifySecMsgWalletUnlocked;
|
||||
|
||||
bool fSecMsgEnabled = false;
|
||||
|
||||
@@ -94,13 +97,163 @@ uint32_t nPeerIdCounter = 1;
|
||||
CCriticalSection cs_smsg;
|
||||
CCriticalSection cs_smsgDB;
|
||||
|
||||
leveldb::DB *smsgDB = NULL;
|
||||
rocksdb::DB *smsgDB = nullptr;
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// rocksdb::DB::Open shipped the raw DB** overload for years, then added a
|
||||
// std::unique_ptr<DB>* form in 8.x and removed the raw form in newer
|
||||
// releases (Homebrew's macOS rocksdb hits this path; Ubuntu 22.04 and
|
||||
// MSYS2 still expose the DB** form). Pick whichever overload the linked
|
||||
// rocksdb actually has via SFINAE — `int` parameter is preferred over
|
||||
// `long`, so if DB** exists, the first overload wins; otherwise the
|
||||
// fallback that wraps unique_ptr is used.
|
||||
template<typename T>
|
||||
inline auto OpenSmsgDBImpl(const rocksdb::Options& opts, const std::string& path,
|
||||
T** dbptr, int)
|
||||
-> decltype(rocksdb::DB::Open(opts, path, dbptr))
|
||||
{
|
||||
return rocksdb::DB::Open(opts, path, dbptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline rocksdb::Status OpenSmsgDBImpl(const rocksdb::Options& opts, const std::string& path,
|
||||
T** dbptr, long)
|
||||
{
|
||||
std::unique_ptr<T> tmp;
|
||||
auto s = rocksdb::DB::Open(opts, path, &tmp);
|
||||
if (s.ok()) *dbptr = tmp.release();
|
||||
return s;
|
||||
}
|
||||
|
||||
inline rocksdb::Status OpenSmsgDB(const rocksdb::Options& opts,
|
||||
const std::string& path,
|
||||
rocksdb::DB** dbptr)
|
||||
{
|
||||
return OpenSmsgDBImpl(opts, path, dbptr, 0);
|
||||
}
|
||||
|
||||
// ── smsgDB leveldb → rocksdb migration ──────────────────────────────────────
|
||||
//
|
||||
// Pre-v5.10 the smessage store was backed by LevelDB at <datadir>/smsgDB/.
|
||||
// Phase 3a switched it to RocksDB. Existing installations need their pubkey
|
||||
// cache and inbox/outbox to carry across. The migration is one-shot and
|
||||
// runs lazily inside SecMsgDB::Open: detect the legacy format, rename the
|
||||
// directory aside as a backup, copy every key into a fresh rocksdb tree,
|
||||
// then proceed with the normal open path. The leveldb backup is preserved
|
||||
// (never deleted) so the user can roll back by deleting smsgDB/ and
|
||||
// renaming smsgDB.leveldb-backup/ back.
|
||||
|
||||
// LevelDB and RocksDB share several filenames (CURRENT, MANIFEST-*, LOG).
|
||||
// RocksDB additionally writes IDENTITY and OPTIONS-* on first open;
|
||||
// presence of CURRENT *without* IDENTITY indicates a legacy LevelDB tree.
|
||||
inline bool IsLegacyLevelDbSmsgDir(const fs::path& dir)
|
||||
{
|
||||
if (!fs::exists(dir / "CURRENT"))
|
||||
return false;
|
||||
if (fs::exists(dir / "IDENTITY"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool MigrateSmsgDBLevelDbToRocksDb(std::string& strError)
|
||||
{
|
||||
fs::path smsgDir = GetDataDir() / "smsgDB";
|
||||
fs::path backupDir = GetDataDir() / "smsgDB.leveldb-backup";
|
||||
|
||||
if (fs::exists(backupDir)) {
|
||||
strError = "smsgDB.leveldb-backup/ already present at " + backupDir.string()
|
||||
+ " — manual cleanup required before retrying migration.";
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("smessage: migrating LevelDB-format smsgDB to RocksDB...\n");
|
||||
|
||||
// Atomic rename so the legacy data is never deleted by this routine —
|
||||
// worst case we leave the backup and bail. Filesystem rename within the
|
||||
// same datadir is atomic on every supported platform.
|
||||
std::error_code ec;
|
||||
fs::rename(smsgDir, backupDir, ec);
|
||||
if (ec) {
|
||||
strError = "Failed to rename smsgDB to backup: " + ec.message();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open backup as leveldb (read-only) — create_if_missing left at default
|
||||
// false so a malformed dir errors out cleanly.
|
||||
leveldb::Options leveldbOpts;
|
||||
leveldb::DB* oldDb = nullptr;
|
||||
leveldb::Status ls = leveldb::DB::Open(leveldbOpts, backupDir.string(), &oldDb);
|
||||
if (!ls.ok()) {
|
||||
strError = "Failed to open legacy LevelDB smsgDB at "
|
||||
+ backupDir.string() + ": " + ls.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open destination as fresh rocksdb.
|
||||
rocksdb::Options rdbOpts;
|
||||
rdbOpts.create_if_missing = true;
|
||||
rocksdb::DB* newDb = nullptr;
|
||||
rocksdb::Status rs = OpenSmsgDB(rdbOpts, smsgDir.string(), &newDb);
|
||||
if (!rs.ok()) {
|
||||
delete oldDb;
|
||||
strError = "Failed to create new RocksDB smsgDB: " + rs.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy every key in batches of 5000.
|
||||
leveldb::Iterator* it = oldDb->NewIterator(leveldb::ReadOptions());
|
||||
rocksdb::WriteBatch batch;
|
||||
int nMigrated = 0;
|
||||
int nBatch = 0;
|
||||
bool fOk = true;
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
batch.Put(it->key().ToString(), it->value().ToString());
|
||||
nBatch++;
|
||||
nMigrated++;
|
||||
if (nBatch >= 5000) {
|
||||
rocksdb::Status ws = newDb->Write(rocksdb::WriteOptions(), &batch);
|
||||
if (!ws.ok()) {
|
||||
strError = "RocksDB batch write failed during migration: " + ws.ToString();
|
||||
fOk = false;
|
||||
break;
|
||||
}
|
||||
batch.Clear();
|
||||
nBatch = 0;
|
||||
}
|
||||
}
|
||||
if (fOk && nBatch > 0) {
|
||||
rocksdb::Status ws = newDb->Write(rocksdb::WriteOptions(), &batch);
|
||||
if (!ws.ok()) {
|
||||
strError = "RocksDB final batch write failed: " + ws.ToString();
|
||||
fOk = false;
|
||||
}
|
||||
}
|
||||
if (fOk && !it->status().ok()) {
|
||||
strError = "LevelDB iterator failed mid-migration: " + it->status().ToString();
|
||||
fOk = false;
|
||||
}
|
||||
delete it;
|
||||
delete oldDb;
|
||||
delete newDb;
|
||||
|
||||
if (!fOk) {
|
||||
// Leave smsgDB/ in a partially-written state but the backup is
|
||||
// intact. The user can recover by removing smsgDB/ and renaming
|
||||
// smsgDB.leveldb-backup/ → smsgDB/.
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("smessage: migrated %d entries from LevelDB to RocksDB. "
|
||||
"Original data preserved at %s\n",
|
||||
nMigrated, backupDir.string().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
const long int SMSG_BUCKET_FILE_SIZE_LIMIT = 0x70000000L;
|
||||
const int64_t SMSG_THREAD_SHUTDOWN_WAIT_MS = 5000;
|
||||
const int64_t SMSG_THREAD_SHUTDOWN_POLL_MS = 50;
|
||||
@@ -137,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);
|
||||
@@ -211,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(),
|
||||
@@ -316,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);
|
||||
@@ -345,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);
|
||||
@@ -390,9 +543,9 @@ bool SecMsgDB::Open(const char* pszMode)
|
||||
};
|
||||
|
||||
bool fCreate = strchr(pszMode, 'c');
|
||||
|
||||
|
||||
fs::path fullpath = GetDataDir() / "smsgDB";
|
||||
|
||||
|
||||
if (!fCreate
|
||||
&& (!fs::exists(fullpath)
|
||||
|| !fs::is_directory(fullpath)))
|
||||
@@ -400,10 +553,21 @@ bool SecMsgDB::Open(const char* pszMode)
|
||||
printf("SecMsgDB::open() - DB does not exist.\n");
|
||||
return false;
|
||||
};
|
||||
|
||||
leveldb::Options options;
|
||||
|
||||
// One-shot migration: pre-v5.10 nodes have a LevelDB tree under smsgDB/.
|
||||
// Detect that and convert to RocksDB before opening. The legacy data is
|
||||
// renamed to smsgDB.leveldb-backup/ as a recovery option.
|
||||
if (fs::is_directory(fullpath) && IsLegacyLevelDbSmsgDir(fullpath)) {
|
||||
std::string migrateError;
|
||||
if (!MigrateSmsgDBLevelDbToRocksDb(migrateError)) {
|
||||
printf("SecMsgDB::open() - migration failed: %s\n", migrateError.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb::Options options;
|
||||
options.create_if_missing = fCreate;
|
||||
leveldb::Status s = leveldb::DB::Open(options, fullpath.string(), &smsgDB);
|
||||
rocksdb::Status s = OpenSmsgDB(options, fullpath.string(), &smsgDB);
|
||||
|
||||
if (!s.ok())
|
||||
{
|
||||
@@ -417,66 +581,37 @@ bool SecMsgDB::Open(const char* pszMode)
|
||||
};
|
||||
|
||||
|
||||
class SecMsgBatchScanner : public leveldb::WriteBatch::Handler
|
||||
{
|
||||
public:
|
||||
std::string needle;
|
||||
bool* deleted;
|
||||
std::string* foundValue;
|
||||
bool foundEntry;
|
||||
|
||||
SecMsgBatchScanner() : foundEntry(false) {}
|
||||
|
||||
virtual void Put(const leveldb::Slice& key, const leveldb::Slice& value)
|
||||
{
|
||||
if (key.ToString() == needle)
|
||||
{
|
||||
foundEntry = true;
|
||||
*deleted = false;
|
||||
*foundValue = value.ToString();
|
||||
};
|
||||
};
|
||||
|
||||
virtual void Delete(const leveldb::Slice& key)
|
||||
{
|
||||
if (key.ToString() == needle)
|
||||
{
|
||||
foundEntry = true;
|
||||
*deleted = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// When performing a read, if we have an active batch we need to check it first
|
||||
// before reading from the database, as the rest of the code assumes that once
|
||||
// a database transaction begins reads are consistent with it. It would be good
|
||||
// to change that assumption in future and avoid the performance hit, though in
|
||||
// practice it does not appear to be large.
|
||||
// a database transaction begins reads are consistent with it.
|
||||
//
|
||||
// Previously implemented via rocksdb::WriteBatch::Handler subclass, which fails
|
||||
// to link against Ubuntu's librocksdb-dev (typeinfo for the Handler base class
|
||||
// isn't exported there). The pendingBatch map is updated alongside every Put
|
||||
// or Delete on activeBatch and answers ScanBatch queries directly.
|
||||
bool SecMsgDB::ScanBatch(const CDataStream& key, std::string* value, bool* deleted) const
|
||||
{
|
||||
if (!activeBatch)
|
||||
return false;
|
||||
|
||||
|
||||
*deleted = false;
|
||||
SecMsgBatchScanner scanner;
|
||||
scanner.needle = key.str();
|
||||
scanner.deleted = deleted;
|
||||
scanner.foundValue = value;
|
||||
leveldb::Status s = activeBatch->Iterate(&scanner);
|
||||
if (!s.ok())
|
||||
{
|
||||
printf("SecMsgDB ScanBatch error: %s\n", s.ToString().c_str());
|
||||
auto it = pendingBatch.find(key.str());
|
||||
if (it == pendingBatch.end())
|
||||
return false;
|
||||
};
|
||||
|
||||
return scanner.foundEntry;
|
||||
if (!it->second.has_value()) {
|
||||
*deleted = true;
|
||||
return true;
|
||||
}
|
||||
*value = *it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecMsgDB::TxnBegin()
|
||||
{
|
||||
if (activeBatch)
|
||||
return true;
|
||||
activeBatch = new leveldb::WriteBatch();
|
||||
activeBatch = new rocksdb::WriteBatch();
|
||||
pendingBatch.clear();
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -484,12 +619,13 @@ bool SecMsgDB::TxnCommit()
|
||||
{
|
||||
if (!activeBatch)
|
||||
return false;
|
||||
|
||||
leveldb::WriteOptions writeOptions;
|
||||
|
||||
rocksdb::WriteOptions writeOptions;
|
||||
writeOptions.sync = true;
|
||||
leveldb::Status status = pdb->Write(writeOptions, activeBatch);
|
||||
rocksdb::Status status = pdb->Write(writeOptions, activeBatch);
|
||||
delete activeBatch;
|
||||
activeBatch = NULL;
|
||||
activeBatch = nullptr;
|
||||
pendingBatch.clear();
|
||||
|
||||
if (!status.ok())
|
||||
{
|
||||
@@ -503,7 +639,8 @@ bool SecMsgDB::TxnCommit()
|
||||
bool SecMsgDB::TxnAbort()
|
||||
{
|
||||
delete activeBatch;
|
||||
activeBatch = NULL;
|
||||
activeBatch = nullptr;
|
||||
pendingBatch.clear();
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -531,12 +668,12 @@ bool SecMsgDB::ReadPK(CKeyID& addr, CPubKey& pubkey)
|
||||
|
||||
if (readFromDb)
|
||||
{
|
||||
leveldb::Status s = pdb->Get(leveldb::ReadOptions(), ssKey.str(), &strValue);
|
||||
rocksdb::Status s = pdb->Get(rocksdb::ReadOptions(), ssKey.str(), &strValue);
|
||||
if (!s.ok())
|
||||
{
|
||||
if (s.IsNotFound())
|
||||
return false;
|
||||
printf("LevelDB read failure: %s\n", s.ToString().c_str());
|
||||
printf("RocksDB read failure: %s\n", s.ToString().c_str());
|
||||
return false;
|
||||
};
|
||||
};
|
||||
@@ -569,18 +706,19 @@ bool SecMsgDB::WritePK(CKeyID& addr, CPubKey& pubkey)
|
||||
if (activeBatch)
|
||||
{
|
||||
activeBatch->Put(ssKey.str(), ssValue.str());
|
||||
pendingBatch[ssKey.str()] = ssValue.str();
|
||||
return true;
|
||||
};
|
||||
|
||||
leveldb::WriteOptions writeOptions;
|
||||
|
||||
rocksdb::WriteOptions writeOptions;
|
||||
writeOptions.sync = true;
|
||||
leveldb::Status s = pdb->Put(writeOptions, ssKey.str(), ssValue.str());
|
||||
rocksdb::Status s = pdb->Put(writeOptions, ssKey.str(), ssValue.str());
|
||||
if (!s.ok())
|
||||
{
|
||||
printf("SecMsgDB write failure: %s\n", s.ToString().c_str());
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -605,12 +743,12 @@ bool SecMsgDB::ExistsPK(CKeyID& addr)
|
||||
};
|
||||
};
|
||||
|
||||
leveldb::Status s = pdb->Get(leveldb::ReadOptions(), ssKey.str(), &unused);
|
||||
rocksdb::Status s = pdb->Get(rocksdb::ReadOptions(), ssKey.str(), &unused);
|
||||
return s.IsNotFound() == false;
|
||||
};
|
||||
|
||||
|
||||
bool SecMsgDB::NextSmesg(leveldb::Iterator* it, std::string& prefix, unsigned char* chKey, SecMsgStored& smsgStored)
|
||||
bool SecMsgDB::NextSmesg(rocksdb::Iterator* it, std::string& prefix, unsigned char* chKey, SecMsgStored& smsgStored)
|
||||
{
|
||||
if (!pdb)
|
||||
return false;
|
||||
@@ -638,7 +776,7 @@ bool SecMsgDB::NextSmesg(leveldb::Iterator* it, std::string& prefix, unsigned ch
|
||||
return true;
|
||||
};
|
||||
|
||||
bool SecMsgDB::NextSmesgKey(leveldb::Iterator* it, std::string& prefix, unsigned char* chKey)
|
||||
bool SecMsgDB::NextSmesgKey(rocksdb::Iterator* it, std::string& prefix, unsigned char* chKey)
|
||||
{
|
||||
if (!pdb)
|
||||
return false;
|
||||
@@ -679,12 +817,12 @@ bool SecMsgDB::ReadSmesg(unsigned char* chKey, SecMsgStored& smsgStored)
|
||||
|
||||
if (readFromDb)
|
||||
{
|
||||
leveldb::Status s = pdb->Get(leveldb::ReadOptions(), ssKey.str(), &strValue);
|
||||
rocksdb::Status s = pdb->Get(rocksdb::ReadOptions(), ssKey.str(), &strValue);
|
||||
if (!s.ok())
|
||||
{
|
||||
if (s.IsNotFound())
|
||||
return false;
|
||||
printf("LevelDB read failure: %s\n", s.ToString().c_str());
|
||||
printf("RocksDB read failure: %s\n", s.ToString().c_str());
|
||||
return false;
|
||||
};
|
||||
};
|
||||
@@ -713,18 +851,19 @@ bool SecMsgDB::WriteSmesg(unsigned char* chKey, SecMsgStored& smsgStored)
|
||||
if (activeBatch)
|
||||
{
|
||||
activeBatch->Put(ssKey.str(), ssValue.str());
|
||||
pendingBatch[ssKey.str()] = ssValue.str();
|
||||
return true;
|
||||
};
|
||||
|
||||
leveldb::WriteOptions writeOptions;
|
||||
|
||||
rocksdb::WriteOptions writeOptions;
|
||||
writeOptions.sync = true;
|
||||
leveldb::Status s = pdb->Put(writeOptions, ssKey.str(), ssValue.str());
|
||||
rocksdb::Status s = pdb->Put(writeOptions, ssKey.str(), ssValue.str());
|
||||
if (!s.ok())
|
||||
{
|
||||
printf("SecMsgDB write failed: %s\n", s.ToString().c_str());
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -746,7 +885,7 @@ bool SecMsgDB::ExistsSmesg(unsigned char* chKey)
|
||||
};
|
||||
};
|
||||
|
||||
leveldb::Status s = pdb->Get(leveldb::ReadOptions(), ssKey.str(), &unused);
|
||||
rocksdb::Status s = pdb->Get(rocksdb::ReadOptions(), ssKey.str(), &unused);
|
||||
return s.IsNotFound() == false;
|
||||
return true;
|
||||
};
|
||||
@@ -759,12 +898,13 @@ bool SecMsgDB::EraseSmesg(unsigned char* chKey)
|
||||
if (activeBatch)
|
||||
{
|
||||
activeBatch->Delete(ssKey.str());
|
||||
pendingBatch[ssKey.str()] = std::nullopt;
|
||||
return true;
|
||||
};
|
||||
|
||||
leveldb::WriteOptions writeOptions;
|
||||
|
||||
rocksdb::WriteOptions writeOptions;
|
||||
writeOptions.sync = true;
|
||||
leveldb::Status s = pdb->Delete(writeOptions, ssKey.str());
|
||||
rocksdb::Status s = pdb->Delete(writeOptions, ssKey.str());
|
||||
|
||||
if (s.ok() || s.IsNotFound())
|
||||
return true;
|
||||
@@ -881,7 +1021,7 @@ void ThreadSecureMsgPow(void* parg)
|
||||
// -- sleep at end, then fSecMsgEnabled is tested on wake
|
||||
|
||||
SecMsgDB dbOutbox;
|
||||
leveldb::Iterator* it;
|
||||
rocksdb::Iterator* it;
|
||||
{
|
||||
LOCK(cs_smsgDB);
|
||||
|
||||
@@ -889,7 +1029,7 @@ void ThreadSecureMsgPow(void* parg)
|
||||
continue;
|
||||
|
||||
// -- fifo (smallest key first)
|
||||
it = dbOutbox.pdb->NewIterator(leveldb::ReadOptions());
|
||||
it = dbOutbox.pdb->NewIterator(rocksdb::ReadOptions());
|
||||
}
|
||||
// -- break up lock, SecureMsgSetHash will take long
|
||||
|
||||
@@ -1202,7 +1342,7 @@ int SecureMsgReadIni()
|
||||
continue;
|
||||
|
||||
if (!(pName = strtok(cLine, "="))
|
||||
|| !(pValue = strtok(NULL, "=")))
|
||||
|| !(pValue = strtok(nullptr, "=")))
|
||||
continue;
|
||||
|
||||
if (strcmp(pName, "newAddressRecv") == 0)
|
||||
@@ -1336,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;
|
||||
@@ -1367,7 +1507,7 @@ bool SecureMsgShutdown()
|
||||
{
|
||||
LOCK(cs_smsgDB);
|
||||
delete smsgDB;
|
||||
smsgDB = NULL;
|
||||
smsgDB = nullptr;
|
||||
};
|
||||
|
||||
// -- main program will wait 5 seconds for threads to terminate.
|
||||
@@ -1411,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;
|
||||
@@ -1482,7 +1622,7 @@ bool SecureMsgDisable()
|
||||
{
|
||||
LOCK(cs_smsgDB);
|
||||
delete smsgDB;
|
||||
smsgDB = NULL;
|
||||
smsgDB = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -2238,7 +2378,7 @@ bool SecureMsgScanBlock(CBlock& block)
|
||||
|
||||
{
|
||||
LOCK(cs_smsgDB);
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
|
||||
SecMsgDB addrpkdb;
|
||||
if (!addrpkdb.Open("cw")
|
||||
@@ -2277,7 +2417,7 @@ bool ScanChainForPublicKeys(CBlockIndex* pindexStart)
|
||||
{
|
||||
LOCK(cs_smsgDB);
|
||||
|
||||
CTxDB txdb("r");
|
||||
auto txdb_holder = MakeChainDB("r"); CTxDBBase& txdb = *txdb_holder;
|
||||
|
||||
SecMsgDB addrpkdb;
|
||||
if (!addrpkdb.Open("cw")
|
||||
@@ -2313,7 +2453,7 @@ bool SecureMsgScanBlockChain()
|
||||
if (lockMain)
|
||||
{
|
||||
CBlockIndex *pindexScan = pindexGenesisBlock;
|
||||
if (pindexScan == NULL)
|
||||
if (pindexScan == nullptr)
|
||||
{
|
||||
printf("Error: pindexGenesisBlock not set.\n");
|
||||
return false;
|
||||
@@ -2472,7 +2612,7 @@ bool SecureMsgScanBuckets()
|
||||
// -- remove wl file when scanned
|
||||
try {
|
||||
fs::remove((*itd).path());
|
||||
} catch (const boost::filesystem::filesystem_error& ex)
|
||||
} catch (const std::filesystem::filesystem_error& ex)
|
||||
{
|
||||
printf("Error removing wl file %s - %s\n", fileName.c_str(), ex.what());
|
||||
return 1;
|
||||
@@ -2552,7 +2692,7 @@ int SecureMsgWalletUnlocked()
|
||||
printf("Dropping wallet locked file %s, expired.\n", fileName.c_str());
|
||||
try {
|
||||
fs::remove((*itd).path());
|
||||
} catch (const boost::filesystem::filesystem_error& ex)
|
||||
} catch (const std::filesystem::filesystem_error& ex)
|
||||
{
|
||||
printf("Error removing wl file %s - %s\n", fileName.c_str(), ex.what());
|
||||
return 1;
|
||||
@@ -2620,7 +2760,7 @@ int SecureMsgWalletUnlocked()
|
||||
// -- remove wl file when scanned
|
||||
try {
|
||||
fs::remove((*itd).path());
|
||||
} catch (const boost::filesystem::filesystem_error& ex)
|
||||
} catch (const std::filesystem::filesystem_error& ex)
|
||||
{
|
||||
printf("Error removing wl file %s - %s\n", fileName.c_str(), ex.what());
|
||||
return 1;
|
||||
@@ -3119,7 +3259,7 @@ int SecureMsgStoreUnscanned(unsigned char *pHeader, unsigned char *pPayload, uin
|
||||
try {
|
||||
pathSmsgDir = GetDataDir() / "smsgStore";
|
||||
fs::create_directory(pathSmsgDir);
|
||||
} catch (const boost::filesystem::filesystem_error& ex)
|
||||
} catch (const std::filesystem::filesystem_error& ex)
|
||||
{
|
||||
printf("Error: Failed to create directory %s - %s\n", pathSmsgDir.string().c_str(), ex.what());
|
||||
return 1;
|
||||
@@ -3209,7 +3349,7 @@ int SecureMsgStore(unsigned char *pHeader, unsigned char *pPayload, uint32_t nPa
|
||||
try {
|
||||
pathSmsgDir = GetDataDir() / "smsgStore";
|
||||
fs::create_directory(pathSmsgDir);
|
||||
} catch (const boost::filesystem::filesystem_error& ex)
|
||||
} catch (const std::filesystem::filesystem_error& ex)
|
||||
{
|
||||
printf("Error: Failed to create directory %s - %s\n", pathSmsgDir.string().c_str(), ex.what());
|
||||
return 1;
|
||||
@@ -3379,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)
|
||||
@@ -3456,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)
|
||||
@@ -3539,7 +3679,7 @@ int SecureMsgEncrypt(SecureMessage& smsg, std::string& addressFrom, std::string&
|
||||
3 addressFrom is invalid.
|
||||
4 addressTo is invalid.
|
||||
5 Could not get public key for addressTo.
|
||||
6 ECDH_compute_key failed
|
||||
6 ECDH key derivation failed
|
||||
7 Could not get private key for addressFrom.
|
||||
8 Could not allocate memory.
|
||||
9 Could not compress message data.
|
||||
@@ -3634,22 +3774,26 @@ int SecureMsgEncrypt(SecureMessage& smsg, std::string& addressFrom, std::string&
|
||||
|
||||
std::vector<unsigned char> vchP;
|
||||
vchP.resize(32);
|
||||
EC_KEY* pkeyr = keyR.GetECKey();
|
||||
EC_KEY* pkeyK = keyK.GetECKey();
|
||||
|
||||
// always seems to be 32, worth checking?
|
||||
//int field_size = EC_GROUP_get_degree(EC_KEY_get0_group(pkeyr));
|
||||
//int secret_len = (field_size+7)/8;
|
||||
//printf("secret_len %d.\n", secret_len);
|
||||
|
||||
// -- ECDH_compute_key returns the same P if fed compressed or uncompressed public keys
|
||||
int lenP = ECDH_compute_key(&vchP[0], 32, EC_KEY_get0_public_key(pkeyK), pkeyr, NULL);
|
||||
|
||||
if (lenP != 32)
|
||||
|
||||
bool fCompressedR = false;
|
||||
CSecret secretR = keyR.GetSecret(fCompressedR);
|
||||
if (secretR.size() != 32)
|
||||
{
|
||||
printf("ECDH_compute_key failed, lenP: %d.\n", lenP);
|
||||
printf("ECDH: keyR secret has unexpected size %zu.\n", secretR.size());
|
||||
return 6;
|
||||
};
|
||||
}
|
||||
std::vector<unsigned char> vchPubK = keyK.GetPubKey().Raw();
|
||||
if (vchPubK.size() != 33 && vchPubK.size() != 65)
|
||||
{
|
||||
printf("ECDH: keyK pubkey has unexpected size %zu.\n", vchPubK.size());
|
||||
return 6;
|
||||
}
|
||||
|
||||
if (!ECDH_xonly_secp256k1(&vchP[0], &secretR[0], &vchPubK[0], vchPubK.size()))
|
||||
{
|
||||
printf("ECDH (encrypt): secp256k1_ecdh failed.\n");
|
||||
return 6;
|
||||
}
|
||||
|
||||
CPubKey cpkR = keyR.GetPubKey();
|
||||
if (!cpkR.IsValid()
|
||||
@@ -3777,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)
|
||||
@@ -3837,7 +3981,7 @@ int SecureMsgSend(std::string& addressFrom, std::string& addressTo, std::string&
|
||||
case 3: sError = "Invalid addressFrom."; break;
|
||||
case 4: sError = "Invalid addressTo."; break;
|
||||
case 5: sError = "Could not get public key for addressTo."; break;
|
||||
case 6: sError = "ECDH_compute_key failed."; break;
|
||||
case 6: sError = "ECDH key derivation failed."; break;
|
||||
case 7: sError = "Could not get private key for addressFrom."; break;
|
||||
case 8: sError = "Could not allocate memory."; break;
|
||||
case 9: sError = "Could not compress message data."; break;
|
||||
@@ -4050,16 +4194,26 @@ int SecureMsgDecrypt(bool fTestOnly, std::string& address, unsigned char *pHeade
|
||||
// -- Do an EC point multiply with private key k and public key R. This gives you public key P.
|
||||
std::vector<unsigned char> vchP;
|
||||
vchP.resize(32);
|
||||
EC_KEY* pkeyk = keyDest.GetECKey();
|
||||
EC_KEY* pkeyR = keyR.GetECKey();
|
||||
|
||||
int lenPdec = ECDH_compute_key(&vchP[0], 32, EC_KEY_get0_public_key(pkeyR), pkeyk, NULL);
|
||||
|
||||
if (lenPdec != 32)
|
||||
|
||||
bool fCompressedDest = false;
|
||||
CSecret secretDest = keyDest.GetSecret(fCompressedDest);
|
||||
if (secretDest.size() != 32)
|
||||
{
|
||||
printf("ECDH_compute_key failed, lenPdec: %d.\n", lenPdec);
|
||||
printf("ECDH: keyDest secret has unexpected size %zu.\n", secretDest.size());
|
||||
return 1;
|
||||
};
|
||||
}
|
||||
std::vector<unsigned char> vchPubR = keyR.GetPubKey().Raw();
|
||||
if (vchPubR.size() != 33 && vchPubR.size() != 65)
|
||||
{
|
||||
printf("ECDH: keyR pubkey has unexpected size %zu.\n", vchPubR.size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ECDH_xonly_secp256k1(&vchP[0], &secretDest[0], &vchPubR[0], vchPubR.size()))
|
||||
{
|
||||
printf("ECDH (decrypt): secp256k1_ecdh failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// -- Use public key P to calculate the SHA512 hash H.
|
||||
@@ -4077,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)
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
#ifndef SEC_MESSAGE_H
|
||||
#define SEC_MESSAGE_H
|
||||
|
||||
#include <leveldb/db.h>
|
||||
#include <leveldb/write_batch.h>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include <rocksdb/db.h>
|
||||
#include <rocksdb/write_batch.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "db.h"
|
||||
#include "util_signal.h"
|
||||
#include "wallet.h"
|
||||
#include "lz4/lz4.h"
|
||||
|
||||
@@ -41,13 +45,13 @@ extern bool fSecMsgEnabled;
|
||||
class SecMsgStored;
|
||||
|
||||
// Inbox db changed, called with lock cs_smsgDB held.
|
||||
extern boost::signals2::signal<void (SecMsgStored& inboxHdr)> NotifySecMsgInboxChanged;
|
||||
extern CSignal<void(SecMsgStored&)> NotifySecMsgInboxChanged;
|
||||
|
||||
// Outbox db changed, called with lock cs_smsgDB held.
|
||||
extern boost::signals2::signal<void (SecMsgStored& outboxHdr)> NotifySecMsgOutboxChanged;
|
||||
extern CSignal<void(SecMsgStored&)> NotifySecMsgOutboxChanged;
|
||||
|
||||
// Wallet Unlocked, called after all messages received while locked have been processed.
|
||||
extern boost::signals2::signal<void ()> NotifySecMsgWalletUnlocked;
|
||||
extern CSignal<void()> NotifySecMsgWalletUnlocked;
|
||||
|
||||
|
||||
class SecMsgBucket;
|
||||
@@ -70,14 +74,14 @@ public:
|
||||
SecureMessage()
|
||||
{
|
||||
nPayload = 0;
|
||||
pPayload = NULL;
|
||||
pPayload = nullptr;
|
||||
};
|
||||
|
||||
~SecureMessage()
|
||||
{
|
||||
if (pPayload)
|
||||
delete[] pPayload;
|
||||
pPayload = NULL;
|
||||
pPayload = nullptr;
|
||||
};
|
||||
|
||||
unsigned char hash[4];
|
||||
@@ -290,7 +294,7 @@ class SecMsgDB
|
||||
public:
|
||||
SecMsgDB()
|
||||
{
|
||||
activeBatch = NULL;
|
||||
activeBatch = nullptr;
|
||||
};
|
||||
|
||||
~SecMsgDB()
|
||||
@@ -313,16 +317,22 @@ public:
|
||||
bool WritePK(CKeyID& addr, CPubKey& pubkey);
|
||||
bool ExistsPK(CKeyID& addr);
|
||||
|
||||
bool NextSmesg(leveldb::Iterator* it, std::string& prefix, unsigned char* vchKey, SecMsgStored& smsgStored);
|
||||
bool NextSmesgKey(leveldb::Iterator* it, std::string& prefix, unsigned char* vchKey);
|
||||
bool NextSmesg(rocksdb::Iterator* it, std::string& prefix, unsigned char* vchKey, SecMsgStored& smsgStored);
|
||||
bool NextSmesgKey(rocksdb::Iterator* it, std::string& prefix, unsigned char* vchKey);
|
||||
bool ReadSmesg(unsigned char* chKey, SecMsgStored& smsgStored);
|
||||
bool WriteSmesg(unsigned char* chKey, SecMsgStored& smsgStored);
|
||||
bool ExistsSmesg(unsigned char* chKey);
|
||||
bool EraseSmesg(unsigned char* chKey);
|
||||
|
||||
leveldb::DB *pdb; // points to the global instance
|
||||
leveldb::WriteBatch *activeBatch;
|
||||
|
||||
rocksdb::DB *pdb; // points to the global instance
|
||||
rocksdb::WriteBatch *activeBatch;
|
||||
|
||||
// Parallel record of every pending write (value) or delete (nullopt) on
|
||||
// activeBatch. Used by ScanBatch to answer "is this key in the active
|
||||
// batch?" without iterating the WriteBatch via Handler — Ubuntu's
|
||||
// librocksdb-dev hides typeinfo for rocksdb::WriteBatch::Handler so a
|
||||
// subclass-based scan fails to link there.
|
||||
std::map<std::string, std::optional<std::string>> pendingBatch;
|
||||
};
|
||||
|
||||
std::string getTimeString(int64_t timestamp, char *buffer, size_t nBuffer);
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
extern std::vector<CNode*> vNodes;
|
||||
extern CCriticalSection cs_vNodes;
|
||||
@@ -353,7 +353,7 @@ bool TryFetchSnapshot(const fs::path& dataDir, int timeoutSec, std::string& strE
|
||||
g_fetch.success = false;
|
||||
// Drop bad file so we don't trick later loaders.
|
||||
CloseDest();
|
||||
boost::system::error_code ec;
|
||||
std::error_code ec;
|
||||
fs::remove(g_fetch.destPath, ec);
|
||||
}
|
||||
g_fetch.finished = true;
|
||||
@@ -362,7 +362,7 @@ bool TryFetchSnapshot(const fs::path& dataDir, int timeoutSec, std::string& strE
|
||||
}
|
||||
}
|
||||
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
bool ok;
|
||||
@@ -374,7 +374,7 @@ bool TryFetchSnapshot(const fs::path& dataDir, int timeoutSec, std::string& strE
|
||||
strError = strprintf("timeout after %d seconds (totalSize=%" PRId64 ", chunks=%" PRIszu ")",
|
||||
timeoutSec, g_fetch.totalSize, g_fetch.received.size());
|
||||
CloseDest();
|
||||
boost::system::error_code ec;
|
||||
std::error_code ec;
|
||||
fs::remove(g_fetch.destPath, ec);
|
||||
}
|
||||
ok = g_fetch.success;
|
||||
@@ -420,7 +420,7 @@ static bool ScanLocalSnapshot()
|
||||
uint256 expectedHash;
|
||||
if (!Checkpoints::GetSnapshotHash(snapHeight, expectedHash)) return false;
|
||||
|
||||
boost::system::error_code ec;
|
||||
std::error_code ec;
|
||||
int64_t sz = (int64_t)fs::file_size(g_localPath, ec);
|
||||
if (ec) return false;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "uint256.h"
|
||||
#include "serialize.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -47,7 +47,7 @@ struct AvailableSnapshot
|
||||
//
|
||||
// Blocks for up to timeoutSec waiting for peers + transfer. Returns true if a
|
||||
// verified snapshot was written, false on timeout/no peer/verification fail.
|
||||
bool TryFetchSnapshot(const boost::filesystem::path& dataDir,
|
||||
bool TryFetchSnapshot(const std::filesystem::path& dataDir,
|
||||
int timeoutSec,
|
||||
std::string& strError);
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ private:
|
||||
int sourceLine;
|
||||
};
|
||||
|
||||
typedef std::vector< std::pair<void*, CLockLocation> > LockStack;
|
||||
using LockStack = std::vector<std::pair<void*, CLockLocation>>;
|
||||
|
||||
static boost::mutex dd_mutex;
|
||||
static std::mutex dd_mutex;
|
||||
static std::map<std::pair<void*, void*>, LockStack> lockorders;
|
||||
static boost::thread_specific_ptr<LockStack> lockstack;
|
||||
static thread_local std::unique_ptr<LockStack> lockstack;
|
||||
|
||||
|
||||
static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2)
|
||||
@@ -74,24 +74,24 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
|
||||
|
||||
static void push_lock(void* c, const CLockLocation& locklocation, bool fTry)
|
||||
{
|
||||
if (lockstack.get() == NULL)
|
||||
if (!lockstack)
|
||||
lockstack.reset(new LockStack);
|
||||
|
||||
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,19 +5,14 @@
|
||||
#ifndef TRIANGLES_SYNC_H
|
||||
#define TRIANGLES_SYNC_H
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/thread/locks.hpp>
|
||||
#include <boost/thread/condition_variable.hpp>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
/** Recursive mutex: supports recursive locking, but no waiting */
|
||||
using CCriticalSection = std::recursive_mutex;
|
||||
|
||||
|
||||
|
||||
/** Wrapped boost mutex: supports recursive locking, but no waiting */
|
||||
typedef boost::recursive_mutex CCriticalSection;
|
||||
|
||||
/** Wrapped boost mutex: supports waiting but not recursive locking */
|
||||
typedef boost::mutex CWaitableCriticalSection;
|
||||
/** Plain mutex: supports waiting but not recursive locking */
|
||||
using CWaitableCriticalSection = std::mutex;
|
||||
|
||||
#ifdef DEBUG_LOCKORDER
|
||||
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
|
||||
@@ -31,12 +26,12 @@ 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
|
||||
{
|
||||
private:
|
||||
boost::unique_lock<Mutex> lock;
|
||||
std::unique_lock<Mutex> lock;
|
||||
public:
|
||||
|
||||
void Enter(const char* pszName, const char* pszFile, int nLine)
|
||||
@@ -77,7 +72,7 @@ public:
|
||||
return lock.owns_lock();
|
||||
}
|
||||
|
||||
CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, boost::defer_lock)
|
||||
CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, std::defer_lock)
|
||||
{
|
||||
if (fTry)
|
||||
TryEnter(pszName, pszFile, nLine);
|
||||
@@ -96,7 +91,7 @@ public:
|
||||
return lock.owns_lock();
|
||||
}
|
||||
|
||||
boost::unique_lock<Mutex> &GetLock()
|
||||
std::unique_lock<Mutex> &GetLock()
|
||||
{
|
||||
return lock;
|
||||
}
|
||||
@@ -123,15 +118,15 @@ typedef CMutexLock<CCriticalSection> CCriticalBlock;
|
||||
class CSemaphore
|
||||
{
|
||||
private:
|
||||
boost::condition_variable condition;
|
||||
boost::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
std::mutex mutex;
|
||||
int value;
|
||||
|
||||
public:
|
||||
CSemaphore(int init) : value(init) {}
|
||||
|
||||
void wait() {
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
while (value < 1) {
|
||||
condition.wait(lock);
|
||||
}
|
||||
@@ -139,7 +134,7 @@ public:
|
||||
}
|
||||
|
||||
bool try_wait() {
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (value < 1)
|
||||
return false;
|
||||
value--;
|
||||
@@ -148,7 +143,7 @@ public:
|
||||
|
||||
void post() {
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
value++;
|
||||
}
|
||||
condition.notify_one();
|
||||
@@ -187,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)
|
||||
|
||||