43 Commits

Author SHA1 Message Date
6d282db216 consensus: stop BLOCK_VALID_CONTEXT overwriting the block validity level
BLOCK_VALID_CONTEXT was 6. The validity levels above it are sequential VALUES
packed into a 3-bit field, not independent bits, so BLOCK_VALID_MASK is
1|2|3|4|5 == 7 and the value 6 sat entirely inside it. `pindex->nStatus |=
BLOCK_VALID_CONTEXT` (main.cpp:5616, and :3285) therefore did not set a flag --
it overwrote the validity level.

Consequences, all long-standing:
  - A header-only block raised to BLOCK_VALID_TREE(2) became 2|6 == 6, which
    reads as >= BLOCK_VALID_CHAIN(4) and >= BLOCK_VALID_SCRIPTS(5). A block
    merely written to disk reported full script validity.
  - Every later RaiseValidity() silently no-opped, because 6 >= every level.
    ConnectBlock's RaiseValidity(BLOCK_VALID_SCRIPTS) was a permanent no-op.
  - CheckBlockIndex's "CHAIN valid implies all parents are CHAIN valid" invariant
    was violated whenever a stored block sat above a still-header-only ancestor,
    i.e. ordinary out-of-order parallel block download. fDefaultConsistencyChecks
    is true only for regtest, so the abort was regtest-only -- but the garbled
    index is written identically on mainnet, where only the detection is off.

Not a v1.3.0 regression: introduced upstream in Komodo fa309e5b0 (2019-04-02),
inherited via Hush, and byte-identical in v1.0.3, which the production network
runs today. Validity was only ever INFLATED, never deflated, so no valid block
was rejected and no invalid block skipped validation -- ConnectBlock's CheckBlock
and full script/proof verification always ran. The user-visible effects were
misreports: getchaintips labelling never-connected forks "valid-fork",
submitblock answering "duplicate" for unvalidated blocks, and ProcessGetData
serving them. The material cost was to QA: multi-node regtest tests aborted the
syncing node at random, making the rpc-test suite unusable.

Moves the flag to 512, the next free bit above BLOCK_IN_TMPFILE(256), and adds
static_asserts that every nStatus flag is disjoint from BLOCK_VALID_MASK so this
cannot be reintroduced silently. nStatus is serialized as VARINT, so the wider
value needs no format change.

Verified under gdb on regtest. A connected block's nStatus:
  before   0x1e  validity field 6            (measured on the previous binary)
  after   0x21d  validity field 5 = SCRIPTS, context bit set
wallet_sapling.py, which aborted the syncing node deterministically, now runs to
completion with no assert.

No migration: the original level is unrecoverable from a polluted entry (1|6,
3|6 and 5|6 all give 7; 2|6 and 4|6 both give 6), and the only safe guess is
downward, which would risk revalidation work on a 3.25M-block index for a
cosmetic gain. Legacy entries simply have the flag bit clear, so they re-run the
contextual check they previously skipped -- more checking, not less -- and
resolve on any reindex.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
2026-08-31 02:12:17 -05:00
c5fde12485 qa: port 12 wallet/mining tests to python3 and fix three framework blockers
Ports qa/rpc-tests from 6 python3 files to 18. Ran every ported test against
the freshly built v1.3.0 dragonxd. Results, honestly:

  PASS (1)            getblocktemplate_proposals.py
  NOT APPLICABLE (7)  wallet.py, walletbackup.py, wallet_protectcoinbase.py,
                      wallet_listnotes.py, wallet_mergetoaddress.py,
                      getblocktemplate.py, wallet_shieldcoinbase.py
  BLOCKED (4)         wallet_sapling, wallet_nullifiers, wallet_persistence,
                      wallet_treestate

The "not applicable" seven are inherited Zcash/Hush-era tests that exercise
features DragonX deliberately removed. They assume transparent t->t value
transfer, but ASSETCHAINS_PRIVATE=1 (hush_utils.h:1826) makes sendtoaddress
and sendmany consensus-refuse; they assume Sprout joinsplits, which are gone
from the RPC layer entirely; and they hardcode Bitcoin economics (10 coin/block,
100-block maturity) against DragonX's 3 DRGX and COINBASE_MATURITY=1. They are
ported and left in place rather than deleted, but they cannot pass on this chain
without being rewritten around z_shieldcoinbase/autoshield.

Three framework fixes in test_framework/util.py, each of which broke every
multi-node test:
  - initialize_chain() passed -connect=0, and init.cpp soft-sets -listen=0 when
    -connect is present, so cache node0 never opened its p2p port and nodes 1-3
    could never sync to it -- initialize_chain() hung forever in sync_blocks().
    Now passes -listen=1 -bind=127.0.0.1 -dnsseed=0 explicitly (an explicit arg
    beats SoftSetBoolArg) while keeping the cache nodes off the public network.
  - cache cleanup removed files from <datadir> when dragonxd writes them one
    level deeper into the net-specific <datadir>/regtest.
  - set_node_times() did print("..." + t) with t an int -> TypeError.
  - default binary paths corrected to src/dragonxd and src/dragonx-cli.

The four BLOCKED tests are blocked by a daemon assert, not by the port; see the
follow-up commit/report on BLOCK_VALID_CONTEXT.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
2026-08-31 01:53:43 -05:00
e2e10f6ef8 doc: regenerate man pages from the v1.3.0 binaries
Previously the version strings were restamped by hand in af7d9e230 because
help2man and a built binary were both unavailable. Regenerated properly via
util/gen-manpages.sh against freshly built v1.3.0 binaries, which also picks
up two options that were never documented:

  -sietch-min-zouts=<n>   decoy Sapling outputs added to each z_sendmany
  -stratumtarget=<hex>    pool share target, for solo/low-difficulty mining

NOTE: the version lines read "v1.3.0-af7d9e230" because `git describe` has
no annotated tag to find yet. Once v1.3.0 is tagged annotated, these must be
regenerated once more so they read a clean "v1.3.0" -- that step stays open
on the release checklist.

Generated on seed 176 (glibc 2.35 runs the binaries; help2man is installed
there and not on the primary), with an isolated HOME so nothing touched the
node's datadir. The reindex running on that box was not disturbed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
2026-08-31 00:23:56 -05:00
af7d9e2300 release: bump to v1.3.0 and document the signing step that was never performed
The tree stamped 1.2.0 in both configure.ac and src/clientversion.h, but
v1.2.0 is an annotated tag already pushed at fad05d3ab and dev is 27
commits past it. Both trees therefore produced CLIENT_VERSION 1020050 and
announced an identical /DragonX:1.2.0/ subversion, so a released binary
would have been indistinguishable from the tag on the wire, in
getnetworkinfo, and to the wallet's in-app updater -- destroying the only
provenance check users have: build the tag, compare the binary.

Bump MINOR rather than REVISION: the delta since v1.2.0 adds a subsystem
(RandomX stratum) and a new RPC (stratummine).

  configure.ac, src/clientversion.h  1.2.0 -> 1.3.0 (CLIENT_VERSION 1030050)
  doc/man/*.1                        version strings restamped
  contrib/debian/changelog           1.3.0 entry for the 27 commits
  src/chain.h                        stale "CLIENT_VERSION is 1010050" comment

Man page *content* still needs a real regeneration: util/gen-manpages.sh
requires help2man and built 1.3.0 binaries, so it belongs in the release
build, where it will also pick up the new stratum options.

doc/release-process.md is why v1.1.0 and v1.2.0 were tagged but never
became releases. Followed verbatim it produced a release the wallet
refuses to install:

  - it directed releases to a branch named `dragonx`, which does not
    exist; releases are cut on `master`
  - it never once mentioned signing, yet the updater pins an ed25519 key
    and sets kDaemonRequireSignature = true, so an unsigned release is
    refused outright and every user silently stays on their old daemon
  - it did not require the release tag to be annotated, and genbuild.sh
    calls `git describe` without --tags, so a lightweight tag stamps the
    build `v<older-tag>-<sha>` instead of the release version -- which is
    exactly what happened to v1.0.0 through v1.0.3
  - it referenced util/build-debian-package-ARM.sh, which is not in tree

Adds the signing and checksum-table steps, the annotated-tag requirement
with a `git describe` verification, and the rule that a new version must
exceed every existing tag including unpublished ones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
2026-08-30 22:48:52 -05:00
4cc7e0491a qa: finish the python3 port far enough to build the shared test chain
Follow-up to 5e0a70683, which got start_node() working. Three more
defects sat behind it:

- initialize_chain() builds the 4-node cache with its own daemon
  invocation, which 5e0a70683 did not touch. It therefore still omitted
  -regtest (so those cache nodes ran on MAINNET) and -asmap (so they
  refused to start at all). Every test that uses the cache -- which is
  most of the wallet suite -- died there.

- reindex.py and getblocktemplate_longpoll.py each carried a single
  python2 print statement, which is the whole reason they would not even
  parse under python3. Shebangs updated to match.

The suite still does not pass: initialize_chain hits a remaining py2
str+int concatenation, and getblocktemplate.py reaches a real test
assertion. Both are beyond this commit, but the harness now gets far
enough to start nodes, answer RPC and begin building the shared chain,
which it could not do before.
2026-08-30 21:29:08 -05:00
5e0a706839 qa: repair the rpc-test harness so it can start a DragonX node at all
The integration suite has never run against DragonX. Six independent
defects stacked up, each only visible once the previous was fixed:

1. test_framework used python2 implicit relative imports
   ("from authproxy import ..."), removed in python3, so every test died
   at import. Made explicit relative imports.

2. It wrote ZZZ.conf -- a Komodo assetchain convention -- while DragonX
   reads DRAGONX.conf. The daemon therefore never saw the generated
   config, fell back to mainnet defaults and tried to bind RPC 21769,
   which on a seed node is already held by the real node.

3. start_node() was hard-wired for the -ac_name=ZZZ assetchain tests: it
   took the RPC port from extra_args[3], passed extra_args[0] as argv[0]
   of the CLI, and only wrote a config when extra_args[0] matched. Any
   test that passes no extra args crashed on len(None). The generic path
   now takes the port from rpc_port(i) -- the same helper
   initialize_datadir() already used -- and drives the CLI with -datadir.

4. dragonxd refuses to start without an asmap file, which no test datadir
   had. initialize_datadir() now provisions one.

5. -asmap relative paths resolve against the NET-SPECIFIC datadir, so a
   copy in <datadir> is never found. Pass an absolute path.

6. Worst: -regtest was only ever set as "regtest=1" in the conf file,
   which DragonX ignores. Every "regtest" node therefore ran on MAINNET:
   real genesis, real seeds, real peers. An observed run synced 196,180
   live blocks and 679MB into /tmp before the test timed out. -regtest is
   now passed as a command-line flag, with -connect=0 so an isolated
   regtest node stays off the public network.

With these, nodes start, RPC answers, and tests run to a real result.
They do not all pass yet -- getblocktemplate.py reaches an assertion --
but that is now a test outcome rather than a harness failure.
2026-08-30 19:44:27 -05:00
60d66022f6 regtest: default -checkpoints off so an isolated node leaves IBD
chainparams_commandline() applies the DRAGONX mainnet checkpoint set to
every SMART_CHAIN_SYMBOL=="DRAGONX" network, -regtest included, so an
isolated regtest node (height ~hundreds) sits far below the top checkpoint
(~3.2M). IsInitialBlockDownload() latches true whenever fCheckpointsEnabled
&& height < GetTotalBlocksEstimate(), so regtest was permanently in IBD --
disabling every ChainTip auto-op that gates on !IBD (autoshield, z_sweep,
consolidation) and the below-checkpoint script-check skip, and forcing
every regtest test to pass -checkpoints=0 by hand.

Default -checkpoints to false on regtest (still overridable with
-checkpoints=1). Verified: a fresh regtest node logs "Leaving
InitialBlockDownload" after a few blocks with no flag, and -checkpoints=1
keeps it in IBD as before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGhWvdBSgt6UxxHANr7gfN
2026-08-30 17:26:21 -05:00
3b2aa866aa rpc: honor a command-line rpcpassword across restarts; fix dead -rpcusername key
hush_configfile(), on any restart where the auto-generated DRAGONX.conf
already exists, hard-assigned mapArgs["-rpcpassword"] from the conf and
wrote the username to mapArgs["-rpcusername"] -- a key nothing reads
(InitRPCAuthentication in httprpc.cpp and bitcoin-cli.cpp both read
"-rpcuser"). The hard assignment silently overwrote a -rpcpassword passed
on the command line, so after the first run the effective RPC credentials
became {cmdline-user}:{conf-password}, matching neither the command-line
pair the operator passed nor the full conf pair. Automation or external
clients that connect with the known command-line password broke on every
restart.

Use SoftSetArg for both, so a command-line (or explicitly configured)
value wins and the conf-derived credential is only a fallback. Verified on
regtest: after a restart an external client with the command-line password
gets HTTP 200 and the conf's random password gets 401; the conf-only
operator path (no command-line creds) still authenticates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGhWvdBSgt6UxxHANr7gfN
2026-08-30 16:32:20 -05:00
2d6359ea74 gtest: add job_id parser regression test for the 63-char abort fix
Covers b3e81f1ed: a 63-character mining.submit job_id containing any
non-hex byte must never complete to a 32-byte vector, so the EWBF
completion loop skips it instead of constructing uint256() (which
asserts vch.size()==32 and would abort the daemon from an
unauthenticated client).

Five cases pin the invariant using the real ParseHex/uint256 primitives:
all-spaces, a non-hex byte mid-string, and a non-hex byte at the end
never yield 32 bytes; a genuine 63-hex job_id completes to exactly 32
bytes for all 16 digits; plus ParseHex odd/even-length anchors. Wired
gtest/test_stratum_jobid.cpp into Makefile.gtest.include (5 tests, all
pass; full hush-gtest suite now 18/18).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGhWvdBSgt6UxxHANr7gfN
2026-08-30 16:24:55 -05:00
fa16e740b6 stratum: reject low-diff shares before spending a RandomX hash on them
SubmitBlock validated in the wrong order. After two O(1) length checks it
went straight to CheckRandomXSolution() -- a full ~65ms
randomx_calculate_hash -- and only afterwards tested the share target. So
32 arbitrary bytes from any peer bought a RandomX hash, and the cost was
paid in three bad places at once: on the shared HTTP/RPC libevent thread
(stratum uses EventBase(), the same base ThreadHTTP dispatches, and RPC
replies are posted back onto it), inside the read loop that holds
cs_stratum so BlockWatcher cannot push new work to real miners, and
holding the global cs_randomx_validator that block validation also takes.

One connection writing submit lines pins that thread indefinitely.

Move the share-target test above the RandomX verify. CBlockHeader::
GetHash() is SerializeHash over the header including nSolution, so
meeting the target still requires real SHA256d grinding -- an attacker
now pays for the hash instead of the node. Semantics are unchanged,
including that an empty local_diff parses to zero and still rejects; only
the position moved, and the diagnostics are recomputed locally since the
old message used variables declared further down.

This does not make the submit path cheap, only bounded: at the default
share target the grind is small, so a submit rate limit and moving
SubmitBlock off the event loop are both still wanted.

Also add the authorization check every sibling handler has and
mining.submit lacked. Being explicit about what that is worth:
mining.authorize validates no credential, so the gate is parity and
handshake-ordering, not authentication. The reorder above is the part
that actually bounds an unknown peer.
2026-08-28 23:35:06 -05:00
5f40c8ede0 stratum: stop paying every miner's blocks to whoever asked for work first
CreateNewBlock() builds the stratum template with an OP_FALSE placeholder
in the coinbase, and CustomizeWork() substituted the miner's own payout
address only while that placeholder was still intact. GetWorkUnit() then
wrote the customized coinbase straight back into the shared template:

    current_work.GetBlock().vtx[0] = cb;
    current_work.GetBlock().hashMerkleRoot = ...BuildMerkleTree();

which consumed the placeholder for everyone. The first client to request
work after a tip change therefore captured the template. Every later
client on that job got a mining.notify whose merkle root already
committed to the first client's coinbase, CustomizeWork() was a no-op for
them on submit, and SubmitBlock() read the shared root back -- so a block
found by miner B was accepted paying miner A. It was silent: the daemon
logged "GOT BLOCK!!! by <B>" while the coinbase paid A.

With untrusted miners that is a reward-theft primitive, and it is cheap:
mining.authorize sets m_send_work, so re-sending it in a loop wins the
race after every tip.

Leave the template pristine and derive each client's header from a local
copy, in GetWorkUnit for the notify and again in SubmitBlock from the
coinbase CustomizeWork() just produced for that client. This is the
refactor the TODO removed here was asking for.

CustomizeWork() now stamps the payout script unconditionally, so a
coinbase that somehow arrives already customized can never be inherited
by another miner, and rejects an invalid address rather than silently
building a coinbase that pays no one.

Single-miner behaviour is unchanged, which is why this survived: it is
only observable with two miners on one template.
2026-08-28 23:31:47 -05:00
b3e81f1eda stratum: do not abort the daemon on a malformed 63-character job_id
The "EWBF 31 bytes job_id fix" in stratum_mining_submit completes a
63-character job_id with each hex digit in turn and feeds the result
straight to uint256(). ParseHex() stops at the first non-hex character
and returns a shorter vector without signalling an error, and
base_blob(const std::vector<unsigned char>&) asserts vch.size() == 32.

So a single mining.submit whose job_id is any 63-character string
containing a non-hex byte -- 63 spaces will do -- aborts the node.
asserts are live in release builds here: -DNDEBUG appears only in
leveldb's own makefile, never in configure.ac, and the shipped binary
still carries the assertion string. The dispatch loop catches UniValue
and std::exception; abort() goes through both.

Size-check each candidate before constructing, as ParseUInt256() a few
hundred lines up already does for the ordinary path. If none of the 16
completions parse, ret stays null, misses work_templates, and the
handler returns false exactly as it does for any unknown job.

Not gated on IsHex(job_id_str): 63 is odd and IsHex() requires an even
length, so that test would disable the EWBF path this code exists for.
2026-08-28 23:30:24 -05:00
d05302d450 build: stamp container builds with the real version instead of "-unk"
.dockerignore excludes .git, so util/genbuild.sh finds no repository inside
the container and emits "// No build information available", which
clientversion.cpp renders as the "-unk" suffix. Every binary produced by
./build.sh --linux-compat therefore self-reports "v1.2.0-unk" and cannot be
traced to a commit -- including release artifacts, since this build path is
part of the v1.2.0 tag.

Pre-generating src/obj/build.h does not survive (genbuild rewrites it when the
content differs), and simply un-ignoring .git does not help a linked worktree,
whose .git is a file pointing outside the build context.

So build.sh computes the version on the host, mirroring genbuild.sh rule for
rule -- the nearest tag only when HEAD is that tag and the tree is clean,
otherwise v<VERSION>-<short sha> with a -dirty suffix -- and passes it through
a BUILD_DESC build-arg that Dockerfile.compat exports as DRAGONX_BUILD_DESC.
genbuild.sh honours that variable when set and is otherwise untouched; with
git metadata present it emits a byte-identical build.h.

Every added git call is guarded with || true because build.sh runs under
set -eu -o pipefail: a source tarball, a host without git, or a branch whose
only reachable tags are lightweight (v1.0.1-v1.0.3 are lightweight; v1.1.0 is
the first annotated one) would otherwise abort the build with no diagnostic.
Those cases now degrade to the previous "-unk" behaviour with a warning.

A direct "docker build -f Dockerfile.compat" passes no BUILD_DESC and still
produces -unk; the Dockerfile now says so loudly rather than silently.
2026-08-29 01:39:24 +02:00
2232868d9f stratum: RandomX pool mining support + reference miner (stratummine)
The stratum server was hardcoded for legacy Equihash (1347-byte solution,
sol.begin()+3 offset, CheckEquihashSolution) and was off-by-default with a
"do not use on RandomX" warning. DragonX is RandomX, so external pool mining
was impossible. This wires RandomX end-to-end.

Server (stratum.cpp), branched on ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX:
* GetWorkUnit sets StratumWork.nHeight and, for RandomX, sends the per-height
  RandomX key via a new mining.set_randomx_key message (a miner cannot derive
  it without the chain). The legacy mining.notify format is unchanged.
* SubmitBlock/stratum_mining_submit accept a 32-byte solution (== the RandomX
  hash, used as nSolution verbatim) and validate it with CheckRandomXSolution
  instead of CheckEquihashSolution. Target check (GetHash() < target) and the
  nNonce = extranonce1||extranonce2 assembly are shared with the equihash path.
* -stratumtarget=<hex> overrides the pool share target (default diff-1); lets a
  solo/low-difficulty test miner accept easy shares.
* GetWorkUnit's IsInitialBlockDownload guard is bypassed under -testnode=1 so an
  isolated low-work test chain can serve work.

Reference miner: `stratummine "host" port ("address" timeout)` RPC (rpc/mining.cpp,
POSIX-only). A minimal stratum client that subscribes/authorizes, receives work +
the RandomX key, varies nNonce, hashes with RandomX via GetRandomXInput (byte-
identical to CheckRandomXSolution) and submits a 32-byte solution. Off-the-shelf
Equihash/Monero miners can't speak DragonX's 256-bit-nNonce Zcash header, so this
is the reference implementation. Added to the rpc client arg-conversion table.

Validated: loopback (chain 0->4, accepted every time, verifychain=true) AND a real
2-box LAN run (Linux miner -> Mac stratum server, 3 blocks accepted, verifychain=true).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 20:24:13 -05:00
1745ee4e63 net: fix -connect never dialing its targets (empty-addr IsValid gate)
ConnectNode() rejected the connection on !addrConnect.IsValid() (and
!IsReachable(addrConnect)) BEFORE the pszDest branch. But -connect (and
-addnode host:port / "addnode <host> onetry") reaches ConnectNode with an
empty placeholder addrConnect and the real target in pszDest, resolved
later by ConnectSocketByName(). An empty CAddress is invalid, so every
-connect attempt returned NULL before a socket was ever opened — the peer
logs "trying connection <host>" then "ConnectNode FAILED" and never dials.
(Introduced upstream with BIP155/addrv2; -connect went unused because
normal operation connects via addrman with real, valid addresses.)

Guard both early-return checks with `if (!pszDest)` so they apply only when
dialing addrConnect directly. Connect-by-name now falls through to
ConnectSocketByName() as intended. Direct-address connections (pszDest==NULL,
the addrman path) are unchanged.

Validated: two nodes on one host, B started with `-connect=<A>` exclusively
(no -addnode) now connects to A over TLS, syncs A's chain, and stays
isolated (0 other peers) — previously B connected to nothing. Clean build,
verifychain 4 0 = true.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 19:08:36 -05:00
11766ec61b init: fix Windows build regression — fs::path::c_str() into LogPrint
Phase 4 (cf15b0a39) converted the asmap-file-location debug output from
fprintf(stderr,...) to LogPrint. On Windows, boost::filesystem::path::c_str()
returns const wchar_t*, which tinyformat rejects at compile time
(is_wchar<const wchar_t*> has no tinyformat_wchar_is_not_supported member) —
breaking the mingw cross-build at init.o. The old fprintf accepted wchar_t*
silently (and printed garbage on Windows), so the latent bug only surfaced
once the call became type-checked.

Route all 11 asmap_path.c_str() calls through .string().c_str() so the
argument is a narrow std::string on every platform, matching the existing
correct idiom at pathLockFile.string().c_str() (line ~1662). No behavior
change on POSIX (path::c_str() is already char* there).

Caught by the cross-platform build phase of the 3-box test bring-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 17:04:36 -05:00
004808403f cleanup: dedup addrman Select_ table walk and pow-limit-by-algo selection
Two behavior-preserving refactors flagged by the Phase-6 hygiene scoping:

* addrman: CAddrMan::Select_ contained two ~40-line copies of the same
  bucket-table random walk, differing only in the table (vvTried/vvNew),
  its bucket count, and a log label. Extract the shared loop into
  SelectFromTable_(vvTable, nBucketCount, tableName); Select_ now just
  dispatches to it. Verbatim move — clean compile proves self-containment.

* pow: the "Equihash uses powLimit, everything else uses powAlternate"
  selection was copy-pasted as an if/else into GetNextWorkRequired,
  CalculateNextWorkRequired, and lwmaCalculateNextWorkRequired. Extract
  into PowLimitForAlgo(params). The CheckProofOfWork site (line ~892) is
  left as-is: it has an extra `height <= 1` genesis special-case and is
  NOT the same selection. On DragonX (RandomX) this always returns
  powAlternate, exactly as before.

Validated: full build of dragonxd/cli/tx, isolated self-mine to height
336, verifychain 4 0 -> true (exercises PowLimitForAlgo on every block).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 16:51:16 -05:00
e55c67b0eb wallet: extract ParseSendManyRecipients from z_sendmany
Second slice of the z_sendmany split. The ~60-line loop that parses and
validates the "outputs" array — rejecting unknown keys, bad addresses, memos
on taddrs, oversize memos and negative amounts, and sorting recipients into
taddr/zaddr lists while accumulating nTotalOut — is lifted verbatim into
ParseSendManyRecipients(outputs, branchId, taddrRecipients, zaddrRecipients,
nTotalOut).

Verbatim extract-method (the loop text was moved unchanged, not retyped); the
helper reads only outputs + branchId and writes the three by-reference outputs
the loop already produced, which the clean compile proves self-contained (a
reference to any other z_sendmany local would fail to link). Built clean;
verifychain=true.

Together with the SelectAnyZaddrSource slice, z_sendmany is now ~135 lines
shorter, with source-address resolution and recipient parsing separated out.
Remaining: note selection, Sietch padding, and tx assembly (later slices,
best validated against a synced node since z_sendmany's linkability guard
blocks the send path on a peerless node).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 16:28:46 -05:00
4a2e531cb6 wallet: extract SelectAnyZaddrSource from z_sendmany
First slice of the z_sendmany monolith split (Phase-6 refactor). The
`fromaddress == "z"` case ("spend from any zaddr") was ~76 lines inline: it
gathers the wallet's Sapling notes, sums balances per zaddr, and picks a
random zaddr whose confirmed balance covers total outputs + fee. Lift it
verbatim into a helper `SelectAnyZaddrSource(outputs, params)` that returns
the chosen zaddr (or throws the same JSONRPCError), and call it from
z_sendmany.

Behavior-preserving by construction: the block was moved unchanged, the only
edit being `fromaddress = vPotentialAddresses[...]` -> `return ...` with the
call site doing `fromaddress = SelectAnyZaddrSource(outputs, params)`, so
fromaddress receives the identical value (or the identical throw propagates).
The helper takes only outputs/params + globals, which the clean compile
proves (a reference to any z_sendmany local would not link). Built clean;
verifychain=true.

Note: the runtime happy path could not be exercised on an isolated node —
z_sendmany's private-chain "still syncing" linkability guard fires before the
fromaddress resolution when there are no peers — but a verbatim extract-method
does not require it. z_sendmany is now ~76 lines shorter; further extraction
(recipient parsing, fee/tx assembly) remains for follow-up slices.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 16:21:49 -05:00
c92e69a13f miner: fix cs_main/mempool.cs lock leak on the isStake error paths
CreateNewBlock takes cs_main and mempool.cs unconditionally via
ENTER_CRITICAL_SECTION, and releases them on every return path. Two of those
LEAVE pairs — the notary-pay failure and the TestBlockValidity failure — were
wrapped in `if (!isStake) { LEAVE; LEAVE; }` but still `return(0)`
afterwards, so when isStake is true the function returned with both locks
still held: a lock leak that deadlocks the next cs_main acquirer. The success
and timelock return paths already release unconditionally, so the guard was
simply wrong.

Make both LEAVE pairs unconditional to match the ENTER. Behavior-identical on
DragonX (a RandomX chain where staking/LWMAPOS is off, so isStake is always
false and the LEAVE already ran), and correct for both isStake values.

Verified: a node self-mined thousands of blocks across two mining threads
(each block ENTER/LEAVEs the locks in CreateNewBlock) with height rising
continuously and verifychain=true — a leak would have deadlocked immediately.

The fuller RAII conversion (replace the manual ENTER/LEAVE with a scoped
LOCK2 for exception safety) remains a worthwhile follow-up, but is a larger
change to cs_main handling best done under its own review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 16:05:10 -05:00
81f803948c hygiene: Phase 6 (partial) — fix 7 verified bugs + quick-wins + dedup
From the Phase-6 structural scoping: land the verified behavioral bugs and
the safe quick-wins/dedups now; the large refactors (monolith splits, ~180
header globals) and consensus-adjacent items stay deferred. Built clean;
self-mined; verifychain=true.

BUGS (verified by reading the code):
- net.cpp CNode::Ban: a braceless `if (subNet.Match(...))` left
  `pnode->fDisconnect = true;` OUTSIDE the guard, so banning any one subnet
  marked EVERY connected peer for disconnect (dropped the whole peer set).
  Wrapped the two statements in braces. (LIVE, high severity.)
- rpcdump.cpp importwallet: the `!fGood -> throw "Error adding some keys"`
  check was trapped inside the `if (fRescan)` block, so importwallet with
  rescan=false silently reported success when key import failed. Hoisted the
  check before the rescan branch and cleaned the garbled braces/indentation.
- wallet.cpp CommitTransaction: ignored AddToWallet()'s return, so a failed
  disk-persist of a just-signed spend was swallowed while the tx broadcast.
  Now logs a hard error on failure.
- hush_nSPV_fullnode.h: the UTXOS branch declared `uint8_t filter` while the
  twin TXIDS branch uses `uint32_t filter`; dragon_rwnum switches on
  sizeof(filter), so the utxos path parsed only 1 of 4 wire filter bytes.
  Widened to uint32_t.
- asyncrpcoperation_sweep.cpp: LogPrintf("%s ... %s", one-arg) read a missing
  vararg; added the __func__ argument.
- rpcdump.cpp importprivkey: inner `auto secret_key` shadowed the outer
  uint8_t and changed the type into DecodeCustomSecret; dropped the shadow.
- rpcdump.cpp getrescaninfo: char[8] + sprintf("%.4f") overflows when the
  ratio >= 10.0 (transient reorg); widened to char[16] + snprintf.

QUICK WINS: removed the duplicate DRAGON_MAXSCRIPTSIZE #define; pinned the
dead HUSH3-branch NOTARISATION_SCAN_LIMIT_BLOCKS to 1440; fixed init typos
(fRequestShutdown, RPC warmup).

DEDUP: extracted the 19-line try/catch error-mapping block — copy-pasted
identically into all six async operations — into
AsyncRPCOperation::set_error_from_current_exception(), so the mapping is
edited in one place. Behavior-identical (verified all six blocks were byte-
identical first).

Deferred (endorsed by the scoping, better as their own PRs): addrman Select_
dedup, the pow.cpp powLimit helper (consensus file), the miner CreateNewBlock
lock-asymmetry, the wallet monolith splits, and the ~180-global / consensus-
retarget / Komodo-heritage work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 15:55:00 -05:00
817c6b2d0e hygiene: Phase 5 — correct stale comments + name safe magic numbers
Fifth phase of the code-hygiene remediation, done per-file (18 files) via an
18-agent Workflow with strict guardrails. No consensus or security VALUES
changed (verified: pow/hush_utils/hush_bitcoind/net.h/crypter/stratum have
zero non-comment numeric changes); built clean, self-mined, verifychain=true.

Totals: 57 stale comments corrected, 46 user-facing branding fixes, 6
behavior-preserving named constants, 4 stratum warnings, 77 items deliberately
left (consensus/security values, copyright headers).

- Stale comments -> DragonX: corrected HUSH3/HUSH/Komodo/Equihash/Arrakis
  leftovers that misdescribed the active chain (pow AWT/lwma notes, txdb
  "Equihash solution" -> RandomX, wallet CLTV/overwinter/Sapling@1 notes,
  util datadir provenance, crypter's stale mapSproutSpendingKeys invariant).
  Replaced unprofessional/editorializing comments (profane TODOs, "developers
  are elite") with neutral technical notes, preserving the real design info.
- User-facing branding: dumpwallet header "created by Hush" -> "DragonX";
  RPC help text hushd/hush-cli/hushprivkey/hushaddress/Agama -> dragonx*;
  rebranded provably-dead "HUSH3" symbol-fallback literals to DRAGONX.
- Named constants (same values, non-consensus/non-security): addrman
  peer-selection factors (kChanceFactorGrowth/kChanceScale/deprioritize),
  sietch MIN_ZOUTS, an init notarization-DB cache size.
- Left the security macros (ECC/TFM_TIMING_RESISTANT=420, comment-clarified
  only), the consensus subsidy/commission literals + 128/129 TRANSITION
  (comment only), and copyright headers untouched.
- Added a prominent WARNING that the stratum server is Equihash-era and NOT
  updated for DragonX's RandomX PoW (flagged, not rewritten).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 15:19:22 -05:00
cf15b0a399 hygiene: Phase 4 — route unconditional debug output through LogPrint
Fourth phase of the code-hygiene remediation, addressing the ~1,082 stray
fprintf(stderr)/printf debug calls so consensus and hot paths stop spewing
to stderr/stdout. Done per-file (18 files) with conservative rules; the tree
builds clean, self-mines, and `verifychain` re-validates the whole chain
(pow/txdb/coins/miner paths) with -debug=1 enabling every converted line —
zero tinyformat/format-arg exceptions.

Net across 18 files: 174 commented-out debug lines deleted, 173 unconditional
live prints converted to LogPrint("<cat>",...)/LogPrintf (net/mining/pow/nspv/
zrpc categories, format strings + args preserved exactly), 40 pure-noise or
sensitive prints deleted, and 194 calls DELIBERATELY LEFT (already behind
fDebug/fZdebug guards, or genuine startup/fatal-error output that must reach
the console before logging init).

Notable:
- Deleted sensitive success-path dumps (nSPV SIG_TXHASH + full tx input/
  output/change amounts; kvupdate privkey/pubkey hex) that were writing key
  and amount material straight to stderr/stdout.
- Removed the raw 32-byte target hex dumps in the zawy adaptive-PoW helpers
  and the legacy one-shot `if(height==340000)` HUSH artifact in pow.cpp.
- Converted per-tx relay + ban/banlist (net), per-setgenerate MININGTHREADS
  (rpc/mining), signrawtransaction TXPOW, and per-message nSPV traces.
- Left format/arg-mismatched lines untouched (flagged) to avoid introducing
  tinyformat runtime throws.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 15:02:01 -05:00
267e6f7ad5 consensus: drop the dead CBOPRET price-validation from the coinbase check
Step 0 of the scoped HAC/HUSH3-drop work (the zero-risk, self-contained
piece). ContextualCheckCoinbaseTransaction's only action was calling
hush_opretvalidate() for CBOPRET price-oracle validation, gated on
ASSETCHAINS_CBOPRET. On DragonX that global is always 0 (default 0, only
ever set by -ac_cbopret, which DragonX never passes), so the branch is dead
and the function already returns true for every DragonX coinbase.

Remove the dead branch; the function is now unconditionally valid at this
stage, which is behavior-identical on DragonX. Verified: a node self-mines
and `verifychain` re-validates the whole chain (every coinbase re-checked
through this function) = true. hush_opretvalidate (hush_gateway.h) is now
unreferenced; its removal is part of the larger hush_gateway cleanup, tracked
with the rest of the HAC/HUSH3-drop follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 14:14:41 -05:00
0de30bbbd1 hygiene: Phase 3 follow-up — sweep commented-out debug cruft
Remove ~116 commented-out debug/dead-code lines the audit flagged as noise
(findings F6/F7/F15/F22): the Komodo "%s tikN" step-tracer comments and other
commented-out fprintf/printf/LogPrintf/std::cerr calls threaded through
AppInit2 (init.cpp), CreateNewBlock and the miner loops (miner.cpp), plus a
few commented-out dead-code fragments (sendmany SetLockTime/nLockTime, the
CCtx CC_vinselect random-pick block and AddNormalinputsLocal remote-mypk
redirect, miner adaptive-PoW assignments).

Comments only — no compiled behavior changes; the tree builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 13:25:17 -05:00
b7060c7de0 hygiene: Phase 3 — dead-code excision (~7,100 lines)
Third phase of the code-hygiene remediation: remove provably-dead code.
Each in-file deletion was adversarially verified (a per-finding investigator
plus a skeptic that greps the tree to refute deletability); the sanity grep
confirms zero dangling references, and the tree builds and self-mines clean.

Removed wholesale:
- src/cc/dapps/ (9 files, ~6,939 lines): a git-tracked but never-built Komodo
  DEX / z-migration tool (hushdex.c, zmigrate.c, cJSON.c, ...). Nothing in
  Makefile.am/configure.ac references it.

In-file dead code:
- pow.cpp: the `#ifdef original_algo` oldRT_CST_RST function (the macro is
  never defined in source or build flags) and two `if ( 0 )` debug blocks.
- walletdb.cpp: the "orphaned staking transaction" cleanup path (deadTxns) —
  DragonX is RandomX PoW with no staking, so the guard is always false and the
  block never runs; also drop the now-unused static/extern decls.
- cc/eval.h: the ProcessCC / Eval::ImportCoin / ImportPayout / DisputePayout
  declarations that have no definition anywhere in the tree.
- rpc/crosschain.cpp: the crosschainproof stub RPC (returned {} unconditionally)
  and its registrations in rpc/server.cpp, rpc/server.h, rpc/client.cpp.
- coins.cpp: the commented-out `//TODO: delete` Sprout PushAnchor template.
- wallet.cpp: the permanently-zero KMD `interest2` term in CreateTransaction.
- rpc/net.cpp: hush_longestchain's always-zero `n` var (num > (n>>1) => num>0)
  and its `if ( 0 )` debug branch.
- hush_nSPV.h / hush_nSPV_fullnode.h: three `if ( 0 && ... )` dead debug branches.
- net.cpp, crypter.h, saplingconsolidation.cpp (dup set_error_code),
  shieldcoinbase.cpp (`donation < 0` on a uint8_t), cclib.cpp (unused
  FAUCET2SIZE): single-line dead-code fixes.

Deliberately NOT touched (verification refuted the audit's "dead on DragonX"
premise): the ~2,250-line HUSH3 checkpoint block, the miner notary/timelock
paths, and hush_gateway.h — all reachable at runtime via -ac_name / -ac_*
args (the inherited Komodo assetchain model), and hush_gateway's
hush_opretvalidate is called from ConnectBlock (consensus). Dropping those
requires a deliberate decision to remove HAC/HUSH3 mode, tracked separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 13:20:51 -05:00
f76382f8e7 Merge fix/windows-cross-build into the v1.2.0 line
Brings the two mingw cross-compile build-system fixes (link-group via
AC_SUBST(LINK_GROUP_*), librustzcash staged-name normalization) onto the
v1.2.0 line that now carries Phases 1-2 of the code-hygiene remediation.
The two sides are disjoint (build-system files vs. src/wallet/rpc), so
this is a clean integration; the merged tree is re-verified to build for
Windows (Phase 1/2 source changes had only been built for Linux before).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 12:34:15 -05:00
aff6101987 hygiene: Phase 2 — DragonX-authored cleanup (13 findings)
Second phase of the code-hygiene remediation, covering the items the
DragonX team introduced or the rebrand missed. No consensus behavior
changes; validated on a local self-mining node.

Log cruft:
- chainparams: route the startup ">>>>>>>>>>" banner and the port line
  through LogPrintf/LogPrint("net") instead of fprintf(stderr).
- pow: drop the fprintf(stderr) hash-mismatch dump that duplicated the
  LogPrintf copy verbatim.
- miner: gate the RandomXDatasetManager per-alloc/per-VM address dumps and
  MemDiag /proc reads behind LogPrint("randomx"); keep a one-line
  "allocated shared dataset (N GB)" summary at default verbosity.

Named constants (single source of truth in wallet.h):
- DEFAULT_AUTOSHIELD_FEE/INTERVAL, MIN_AUTOSHIELD_INTERVAL,
  DEFAULT_AUTOSHIELD_MIN_UTXOS, AUTOSHIELD_MIN/MAX_FEE for the autoshield
  option parsing and help text (were bare 10000/25/5 literals repeated
  across init.cpp and wallet.h).
- AUTO_OP_TARGET_HEIGHT_OFFSET replaces the three copy-pasted
  `blockHeight + 5` async-op scheduling offsets.
- AUTO_OP_EXPIRY_DELTA replaces the three per-file *_EXPIRY_DELTA=15
  constants (sweep/consolidation/autoshield) with one shared value.
- Move DEFAULT_AUTOSHIELD_FEE out of the op header into wallet.h so it no
  longer collides when both headers are included.

Error surfacing:
- init: report clamped/out-of-range -autoshieldinterval/-autoshieldfee via
  InitWarning() (surfaces to GUI/log) instead of fprintf(stderr).

Rebrand / dead foreign-chain code (approved removals):
- server: drop the HUSH3 special-cases in stop() and HelpExampleCli; the
  cli example now shows "dragonx-cli" instead of "hush-cli".
- getinfo (misc): remove the stale dPoW notarization block (notarized,
  prevMoMheight, notarizedhash, notarizedtxid, notarizedtxid_height,
  HUSHnotarized_height, notarized_confirms) — DragonX is a private chain
  from genesis with no active dPoW. Also fixes the hardcoded "HUSH3" that
  made getinfo query a foreign chain's notarization.
- delete the dead Komodo notary RPCs getera/getdragonjson/
  getnotarysendmany/geterablockheights (getera returned 0;
  getnotarysendmany was marked "this is broke") and their registrations.
- remove the dead ASSETCHAINS_EQUIHASH reporting branches in getinfo and
  getmininginfo — DragonX is RandomX-only.

chainparams: document why the upstream Equihash params and the literal
Bitcoin genesis are retained under RandomX (do not "fix" them).

Deferred: the hdSeedOrigin int->string switch in z_autoshieldstatus
(no shared helper exists to reuse; low value).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 12:24:04 -05:00
798eccc624 hygiene: fix seven latent defects from the v1.2.0 code audit
Phase 1 of the code-hygiene remediation. Each is a genuine defect, not
style. The two consensus functions are touched only by provably
behavior-preserving dead-code removal and a comment.

- miner: initialize the anti-spin loop counter (was `int i;` -- the loop
  condition read an indeterminate value, UB) and break once the block
  time advances past the median, which is what the comment intended.
- consensus/upgrades: drop a stray printf() on the NetworkUpgradeState
  path; the following assert already documents the invariant.
- txdb: CBlockTreeDB::Snapshot2's outer catch treated ANY exception as
  normal end-of-iteration and built a snapshot from partial data. Fail
  instead, matching the inner catch the author marked consensus-relevant
  ("we need to exit here if so for consensus code!"). iter->Valid()
  already handles genuine end-of-iteration.
- wallet/rpcwallet + init: -sietch-min-zouts used a "--" key that the arg
  parser (which normalizes --foo to -foo) can never match, so the Sietch
  decoy floor was silently stuck at the default. Use the single-dash key
  so the knob works, and document it in -help.
- hush_bitcoind + hush_utils: remove four unreachable duplicate `else if`
  branches from hush_commission()/hush_block_subsidy() (a second
  `height < 23860000` and a second `height < 27220000` in each). Proven
  identical across 49,591 heights. NB: the dead values hint at an intended
  clean halving schedule that was never wired up; the DEPLOYED schedule
  (two double-steps) is preserved exactly. Changing it is a future
  consensus decision, not this cleanup.
- hush_bitcoind: replace the "likely a bug" halving TODO with an accurate
  note -- INTERVAL is only consumed by a debug fprintf, so the > vs >=
  boundary at HALVING1 has no consensus effect.
- git rm two committed macOS build artifacts (cc/customcc.dylib and
  libcc.dylib) and add the missing .dylib .gitignore rules.

Built clean on Linux; an isolated node self-mined genesis->5113 exercising
the miner and consensus-subsidy paths, and getsnapshot returned normally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 11:35:38 -05:00
ac95106abe build(win): make the mingw cross-compile link and find librustzcash
Three things broke the x86_64-w64-mingw32 build; two are real and fixed
here (the third was a stale-object contamination from building Linux and
Windows in the same tree, resolved by a clean rebuild — not a code fix).

1. Single-pass mingw ld could not resolve the cross-references DragonX
   added between the internal static archives (libbitcoin_util/common
   objects pulling in UniValue; util<->common mutual deps). GNU ld on
   Linux re-scans archives so it never surfaced; ld64 on macOS rejects
   the grouping flag outright. Bracket each binary's _LDADD in
   -Wl,--start-group/--end-group, delivered via AC_SUBST(LINK_GROUP_*)
   so automake does not reject the linker flag inside _LDADD, and left
   empty on every non-Windows target.

2. The Rust build emits the mingw archive as rustzcash.lib, but the
   link line asks for -lrustzcash, i.e. librustzcash.a. Normalize the
   staged filename to librustzcash.a for every host (a no-op on
   Linux/macOS, where the basename was already librustzcash.a).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 03:56:23 -05:00
02b4d03fc6 wallet: low-severity polish from the dev/v1.2.0 review
Follow-up nits surfaced by the multi-agent review of the dragonx..dev delta;
none are correctness/consensus bugs, all are defensive/consistency tidy-ups.
Builds clean; the diff was reviewed across concurrency, scheduler, and
tx-building lenses.

- wallet: default CWallet::fAutoShieldEnabled to false. init.cpp always
  recomputes it (ON only for CREATED/RESTORED seed provenance) before any
  ChainTip round, so this is behaviour-neutral in the normal path and stops a
  CWallet that skips that init from auto-enabling for provenance the gate would
  reject.

- wallet: clamp a loaded hdSeedOrigin to UNKNOWN when out of enum range, so a
  corrupt/hand-edited wallet.dat cannot claim a known-recoverable seed and flip
  autoshield ON.

- wallet: key the sweep and consolidation ops' NU-straddle guard, transaction
  builder height, and expiry off execution-time tipHeight instead of the stale
  enqueue-time targetHeight_ -- matching the autoshield op (65130c312) so the
  builder's consensus-branch selection agrees with the height the tx is signed
  for. (Sweep previously built at targetHeight_ but expired at the live tip.)

- wallet: on the sweep NU-straddle skip, set sweepComplete_ so the round backs
  nextSweep off one interval instead of re-dispatching a fresh sweep op every
  block through the activation window.

- init: clamp -autoshieldinterval below 5 up to the documented minimum of 5,
  rather than silently resetting it to the default 25.

- chainparams: make the ClearSeeds guard an exact "DRAGONX" match instead of a
  7-char prefix, so DRAGONX-prefixed assetchains (e.g. DRAGONX2) no longer
  inherit DragonX's seeds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UtvyqQQSqR64DNEjUEuTmb
2026-08-26 22:10:35 -05:00
fad05d3ab4 doc: regenerate manpages for v1.2.0
They still described v1.0.3-4caf2fc68, so none of the options added
since -- -autoshield and its four companions, -mnemonic,
-mnemonicsaplinggap -- appeared anywhere in them.

Generated from a binary built at a clean tree on an annotated v1.2.0
tag, which is what makes util/genbuild.sh emit BUILD_DESC "v1.2.0"
rather than a version with a commit suffix. Note that genbuild.sh uses
`git describe --abbrev=0`, which ignores lightweight tags; v1.0.0
through v1.0.3 were lightweight, which is why those releases all
reported themselves as v1.0.x-<sha>. Tag releases annotated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
2026-08-25 18:42:18 -05:00
5d3f7e520c packaging: correct the debian changelog and the manpage file list
- dragonx.manpages listed DEBIAN/manpages/*.1, a path nothing creates,
  so dh_installman would fail on it. Point it at doc/man/*.1, which is
  where util/gen-manpages.sh writes and what doc/man/Makefile.am ships.
- Restore the 1.0.1 and 1.0.2 stanzas, reconstructed from the commits
  each tag actually contains. The file jumped 1.0.3 -> 1.0.0.
- The 1.1.0 and 1.0.0 trailers named weekdays that do not match their
  dates ("Thu, 21 Aug 2026" is a Friday; "Mon, 03 Mar 2026" is a
  Tuesday). Replace both with the real v1.1.0 and v1.0.0 tag dates,
  which fixes the weekday and the disagreement with the tag at once.
- Record the 1.2.0 peer-discovery work in its stanza.
- Drop doc/man/hushd.html and doc/man/hush-cli.html: stale Hush-branded
  pages for binaries this tree no longer builds, referenced by nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
2026-08-25 18:36:03 -05:00
092a608fd9 chainparams: do not let other smart chains inherit DragonX's seeds
A smart chain builds its params by copying a base network and
overriding pieces, but nothing ever touched vSeeds/vFixedSeeds. That is
how DRAGONX ran on Hush's seeds -- seed1.hush.is and friends, long
since gone from DNS -- for as long as it did, and it means any other
assetchain started from this binary now inherits DragonX's.

Seeds are per-chain by nature: an address serving one chain is useless
to another, and dialling it is at best wasted effort and at worst a
peer speaking a different protocol. DRAGONX keeps the seeds configured
in CMainParams, which are its own; every other chain starts empty and
relies on -addnode/-connect, which an assetchain operator has to
configure regardless.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
2026-08-25 18:36:03 -05:00
3dd667b127 net: add two seed nodes and reserve names for three more
node6.dragonx.is (13.140.58.251) and node7.dragonx.is (5.104.83.100)
are new full nodes in regions the existing five did not cover. Both go
into the compiled-in fixed-seed list and into the DRAGONX -addnode set.

node8 through node10 are reserved names with no DNS records yet. A
hostname that does not resolve is harmless on this path --
ThreadOpenAddedConnections simply fails to open the connection and
retries on its normal cycle -- and reserving the names in the binary
means a future seed can be brought into the default addnode set by
creating a single DNS record, with no release and no waiting for users
to upgrade. seed.dragonx.is already gives the DNS-seed path that
property; this extends it to the addnode path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
2026-08-25 18:36:03 -05:00
dde6cd810f net: seed from a round-robin DNS record instead of hardcoded hosts
seed.dragonx.is is now an A-record set over the five seed nodes (DNS-only, TTL 300,
created in Cloudflare alongside this change). One lookup returns all of them, and
the set can change -- a node added, a node retired -- with a DNS edit rather than a
release.

That is the actual point. Before this the network's entry points were hardcoded
into the binary twice over: here in vSeeds, and again in the -addnode injection in
hush_utils.h. Adding a sixth node meant shipping a new version and waiting for
users to upgrade.

node1 and node5 stay as static fallbacks against the round-robin record being
mistyped or deleted. They resolve to the same hosts, so that is insurance against a
DNS mistake rather than real redundancy.

Verified end to end on a fresh datadir (empty addrman, real node untouched, and
crucially with NO custom -port -- see below):

  before:  0 addresses found from DNS seeds, 0 handshakes, 1 block (genesis)
  after:   7 addresses found, connection attempts to all five seeds on :21768,
           3 version handshakes, 3296 blocks connected and syncing

The earlier run of this test appeared to fail with 0 handshakes. That was the
harness, not the code: -port overrides ASSETCHAINS_P2PPORT, and net.cpp builds
DNS-seeded addresses as CAddress(CService(ip, ASSETCHAINS_P2PPORT)), so a test node
with a custom port dials every seeded peer on its own port and reaches nothing.
That is the mechanism behind the long-standing "never use a custom -port on a test
node" rule; use -listen=0 instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:22:56 +02:00
499f02a905 net: repair DragonX peer discovery, broken three separate ways
A DRAGONX node had no working peer discovery. Both mechanisms were broken, and a
third bug hid the fact.

1. DNS seeds were Hush's, and all three are dead. chainparams_commandline() sets an
   assetchain's port, magic, blocktime, upgrade heights and checkpoints but never
   touches vSeeds or vFixedSeeds, so DRAGONX silently inherited CMainParams':
   seed1.hush.is, seed2.hush.is and dns.leto.net. None of the three has an A record
   any more -- verified against 1.1.1.1 and 8.8.8.8, with google.com and
   node1..node5.dragonx.is resolving fine from the same host as a control. Replaced
   with the five DragonX node hostnames, which do resolve and do listen.

2. Every fixed seed carried port 0. contrib/seeds/generate-seeds.py documents its
   input as <ip>:<port>, but contrib/seeds/nodes_main.txt held bare IPs, so
   parse_spec() took the port as empty and emitted 0x00,0x00 for all five entries.
   The fixed-seed fallback -- which exists precisely for when DNS seeding yields
   nothing -- was therefore handing out unconnectable addresses. Added the port to
   nodes_main.txt and regenerated; entries now end 0x55,0x08 (21768).

3. ThreadDNSAddressSeed never incremented `found`, so "%d addresses found from DNS
   seeds" printed 0 unconditionally, whether seeding worked or not. That is almost
   certainly why nobody noticed the seeds had gone dead: the one diagnostic that
   would have shown it was hardcoded to say zero.

Verified on a fresh datadir (empty addrman, separate ports, real node untouched):
DNS seeding now reports "5 addresses found from DNS seeds" where it previously
reported 0, and the fixed-seed path adds 5 entries carrying the correct port.

Note on scope: the five hostnames are single-A-record hosts, so each contributes one
address rather than the spread a real seeder returns. A dedicated DNS seeder, or
simply a round-robin A record over the seed set, would be the proper fix and needs
only a DNS change rather than a release. This restores a working discovery path;
it does not make it a good one.

Also corrected the generated header's #endif comment, which said
HUSH_CHAINPARAMSSEEDS_H while the guard is DRAGONX_CHAINPARAMSSEEDS_H.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:11:55 +02:00
4dc57e80b1 build: bump version to 1.2.0
dev and the v1.1.0 tag were version-indistinguishable: both reported
CLIENT_VERSION 1010050, subversion "/DragonX:1.1.0/" and IS_RELEASE=true, because
660678f9b was the last commit to touch a version file and it predates the tag. The
twenty commits since were therefore invisible to every channel a client can query,
and the in-app updater compares exactly those. Only git-describe distinguished
them, and that degrades to "-unk" on a tarball build.

A minor bump rather than a patch: since v1.1.0 the tree gained auto-shield-coinbase
(a new feature, on by default where the seed is known-recoverable), BIP39 seed
phrases as the default for new wallets, the z_autoshieldstatus RPC, and three new
wallet.dat record types. Understating that as 1.1.1 would hide an on-disk format
change from the one place users look.

The published v1.1.0 tag is left where it is. Re-pointing a tag that is already on
the remote breaks anyone who fetched it.

The wallet feature version deliberately stays at FEATURE_LATEST = 60000. The new
records are additive and older binaries skip unknown types harmlessly, while
bumping it would make them refuse the wallet outright with DB_TOO_NEW. The one real
incompatibility, a truncated hdchain record, is self-healing as of a0ccb4be1, so
refusing to load would be strictly worse for the user than what happens today.

Verified: configure.ac and clientversion.h agree; CLIENT_VERSION 1010050 -> 1020050;
build.sh derives 1.2.0; bitcoin-config.h carries CLIENT_VERSION_MINOR 2 after a
reconfigure run with the depends CONFIG_SITE; a full rebuild of src succeeds with 0
errors and both binaries report v1.2.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 21:52:51 +02:00
04ac7c1186 doc: how to build release binaries in containers, for several glibc floors
Binaries built on Ubuntu 22.04 require GLIBC_2.34 and GLIBCXX_3.4.30 and will not
start on Ubuntu 20.04 -- which is four of our five seeds, and an unknown share of
users. The binary the fleet actually runs today needs only GLIBC_2.29, so it was
built somewhere older; seed 176 has since been upgraded to 22.04 and now produces
binaries it is the only seed able to run.

--linux-compat and Dockerfile.compat already solved this (6d56ad854) but were
undocumented outside the build script and pinned to one base image. Parameterise
the base via ARG BASE_IMAGE (default unchanged, so --linux-compat behaves exactly
as before) and document the whole path.

doc/build-containers.md is written to be executed by a person or an agent starting
from a machine with nothing installed: why the glibc direction matters, with the
measured numbers; what already exists in the repo so nobody writes a second build
system; prerequisites and honest cost (~15GB, 4GB RAM, 1-2h per base because
depends/ builds boost, BDB, wolfssl and rust from source); one-target and
multi-target recipes; which base to pick and why 20.04 is the recommended floor
while 18.04 needs verifying (GCC 7 against -std=c++17); a mandatory verification
step with the exact objdump/readelf commands and the expected ceilings; and the
traps.

The traps are the part worth having written down: ETXTBSY when installing over a
running daemon (cp fails even after the process exits -- stage and rename, then
sha256-verify before starting); never touching configure.ac in a configured tree,
because the mtime alone triggers a reconfigure that dies on libdb_cxx; never
blind-touching a path that may not exist, which silently creates stray empty files;
RandomX needing ARCH=default or it emits AVX-512 that SIGILLs the fleet; build-win.sh
silently discarding every argument; and macOS being uncontainerisable because
depends/ has no darwin cross path at all.

Also records that full static linking is NOT the answer here: the daemon resolves
node1..node5.dragonx.is via getaddrinfo, and static glibc pushes that through NSS,
which dlopens libnss_dns at runtime and reintroduces the dependency it was meant to
remove.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 21:28:12 +02:00
65130c3120 async/wallet: close the loose ends around automated operations
Six small defects, all found by an audit of the automated-operation path and all
verified present before changing anything.

AsyncRPCQueue::addOperation returned void and silently dropped the operation when
the queue was closed or finishing. Every caller assumed success: the three
schedulers left their running flag set with nothing in flight (which then blocks
every later round), and z_sendmany / z_shieldcoinbase / z_mergetoaddress returned
an opid for work that would never run -- z_shieldcoinbase and z_mergetoaddress
having already locked their selected coins in the constructor. It now returns
bool; the schedulers release the flag and log, and the three RPCs raise an error
instead of handing back an opid. Coin locks are memory-only, so a refusal at
shutdown reclaims them with the process; the lie about success was the defect.

Nothing ever removed finished automated operations from the queue's map.
popOperationForId is reached only from z_getoperationresult, so on a node running
autoshield every 25 blocks the map grew by one entry per round forever. The
schedulers now pop the operation they just cancelled. The worker already handles
a missing id ("cannot find operation in map, may have been removed",
asyncrpcqueue.cpp), and it releases lock_ before calling main(), so popping under
cs_wallet introduces no lock cycle.

The autoshield operation built its transaction against targetHeight_, the
enqueue-time height, while SetExpiryHeight and the network-upgrade straddle guard
both used tipHeight. Since the builder's height selects the consensus branch id,
the guard was checking a height the transaction was not signed against -- it could
not prevent the failure it exists to prevent. Now tipHeight throughout.

cancel() in the sweep, consolidation and autoshield operations set CANCELLED
unconditionally, dropping the base class's guard entirely. The schedulers cancel
the previous operation when they enqueue the next, so a round that had already
SUCCEEDED got its result relabelled as cancelled. Restored a narrower guard: still
cancellable while READY or EXECUTING (the base class refuses the latter, which
would defeat cancellation here), but a terminal state is left alone.

CommitAutomatedTx dumped the whole transaction to stderr on every commit,
duplicating the LogPrintf that CommitTransaction does one call later. ToString()
emits a line per input, so with the 400-input autoshield cap that was tens of KB
of stderr per round. Removed.

Also corrected a comment that credited the immature-coinbase exclusion to
fOnlySpendable (the argument is fOnlyConfirmed; the exclusion is unconditional in
AvailableCoins), and noted that AUTOSHIELD_CTXIN_P2SH_SIZE is a byte size that
merely happens to share the value 400 with the input cap.

Verified on an isolated regtest chain, 8/8: nine consecutive autoshield rounds
succeed after the builder-height change; the operation map stays at 1 entry across
all nine (it grew one per round before); stderr totals 1608 bytes for the whole
run with zero CommitAutomatedTx dumps, while debug.log still records all nine
commits via CommitTransaction; no round is relabelled cancelled; funds shield
correctly and no coin locks leak.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 19:18:30 +02:00
698bcf9574 build: derive the release version from configure.ac
build.sh hardcoded VERSION="1.0.3" while configure.ac had been at 1.1.0 since
660678f9b. package_release() uses it to name the output directory, so
`./build.sh --all-release` from dev would have emitted
release/dragonx-1.0.3-<platform>/ containing binaries that report 1.1.0 --
mislabelled artifacts, from the one place where the label is what users see.

Read the four _CLIENT_VERSION_* defines out of configure.ac instead, applying the
same suffix rule its _CLIENT_VERSION_SUFFIX m4 uses (build < 25 -> beta, < 50 ->
rc, == 50 -> plain, > 50 -> point release), and abort if any of them cannot be
parsed rather than naming a release directory after an empty string.

SCRIPT_DIR moves above the version block because the lookup needs it.

Verified: derives 1.1.0 from the current tree, and a deliberately unparseable
configure.ac makes it exit 1 with a message instead of guessing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 18:56:18 +02:00
a0ccb4be1d wallet: repair a truncated hdchain record instead of refusing the wallet
A build predating VERSION_HD_TRANSPARENT rewrites the hdchain record with only
its four base fields while leaving nVersion at whatever it read. Our version-gated
reads then run off the end of the stream, ReadKeyValue turns the throw into
fHDChainRead=false, and LoadWallet escalates that to DB_CORRUPT. Verified against
the real v1.0.2-2b011d6ee release binary: a dev wallet, opened once by v1.0.2 and
given a single new sapling address, came back to

  Error reading wallet database: hdchain record is corrupt
  Error loading wallet.dat: HD seed present but the hdchain record is missing or corrupt
  Error loading wallet.dat: Wallet corrupted

Nothing is actually lost there -- the same test showed the seed phrase restoring
the full balance, the autoshield destination, and even the address v1.0.2 had
generated -- but the user is shown "Wallet corrupted" with no hint of that.

Recover instead, for a v1 or v2 record. Everything derivation depends on is either
in the four-field prefix or in the separate hdseed record, and the trailing
counters are self-healing: DeriveNewChildKey and GenerateNewSaplingZKey both skip
indices whose key the wallet already holds, so restarting a counter at 0 re-walks
past existing keys rather than reissuing them. Read the prefix from an untouched
copy of the stream, default the missing tail, log it, and rewrite the record in
full form so the next load is clean.

A record claiming nVersion >= VERSION_HD_MNEMONIC still fails loud: that flag
selects the derivation input, so guessing it wrong yields a different key tree in
silence. No wallet this code has written can be in that state -- every
InstallHDSeed call site passes fMnemonic=false -- so the branch is defensive only.

Also say what to do about it. Both the log line and the init error now name the
remedy (move wallet.dat aside, restart with -mnemonic and -rescan) rather than
stopping at "Wallet corrupted".

Verified on regtest against the real v1.0.2 binary, 14/14: the round-trip that
previously ended in "Wallet corrupted" now loads, logs the repair and the rewrite,
keeps the balance, the autoshield destination and v1.0.2's own address, still
issues distinct fresh t-addresses after the counter reset, needs no repair on the
second load, and remains fully recoverable from the seed phrase.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 18:18:38 +02:00
358011bd54 wallet: resolve the autoshield destination at startup
pwalletMain->autoShieldAddress was only ever written by a round running in the
current process, so on any node without an explicit -autoshieldaddress,
z_autoshieldstatus reported an empty destination from startup until the first
round fired. disabled_reason was empty on that path too (rpcwallet.cpp), so the
RPC showed autoshield true, running false, no address and no explanation -- the
exact silent state z_autoshieldstatus was added to eliminate. On a restored
wallet, which pre-derives the whole -mnemonicsaplinggap window and therefore
always has an in-gap account to pick, the answer was known at startup and simply
not computed.

Split the read-only half of resolveDestination into a free function shared with
init: the configured override if set, else the lowest in-gap account
m/32'/coin'/i' the wallet already holds. init calls it once when autoshield is
enabled and no explicit address was given.

It deliberately does not generate a key. Deriving a fresh sapling account as a
side effect of populating a status field would mutate the wallet to make an RPC
prettier, so step 3 of resolveDestination -- the generation path, which must stay
inside the operation where an unlocked wallet is already established -- is left
where it was. A brand-new wallet holds nothing in-gap, so the field stays empty
there and disabled_reason now says why instead of being blank.

Behaviour is otherwise unchanged: same derivation, same lowest-index-wins rule,
same refusal to trust CKeyMetadata, same caching for the life of the process.

Verified on an isolated regtest chain, 12/12:
  fresh wallet      -> address "", reason "no destination resolved yet; one will
                       be derived from the HD seed on the first round"
  after one round   -> address set, reason empty
  after RESTART     -> address visible with NO block mined since (height 12 both
                       sides), and z_listaddresses still holds exactly 1 address,
                       so init derived nothing
  -autoshieldaddress-> still overrides the derived destination, and the round
                       shields into it

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 17:41:32 +02:00
108 changed files with 2878 additions and 11814 deletions

4
.gitignore vendored
View File

@@ -131,7 +131,6 @@ src/cc/rogue/rogue
src/cc/rogue/rogue.so
src/cc/rogue/test.zip
src/cc/dapps/a.out
src/checkfile
src/foo.zip
@@ -154,14 +153,15 @@ src/rogue.scr
src/cc/rogue/confdefs.h
src/cc/rogue/x64
src/cc/dapps/a.out
src/Makefile.in
doc/man/Makefile.in
Makefile.in
src/libcc.so
src/libcc.dll
src/libcc.dylib
src/cc/customcc.so
src/cc/customcc.dll
src/cc/customcc.dylib
src/HUSH3_7776
REGTEST_7776
src/cc/librogue.so

View File

@@ -1,4 +1,8 @@
FROM ubuntu:20.04
# Base image is parameterised so one Dockerfile can produce binaries for several
# glibc floors: docker build --build-arg BASE_IMAGE=ubuntu:18.04 ...
# The default is unchanged, so `./build.sh --linux-compat` behaves exactly as before.
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE}
ENV DEBIAN_FRONTEND=noninteractive
@@ -23,6 +27,17 @@ RUN rm -rf /build/depends/built /build/depends/work \
&& rm -rf /build/src/cc/*.o /build/src/cc/*.a \
&& rm -f /build/config.status /build/config.log
# The build context excludes .git (see .dockerignore), so genbuild.sh cannot derive
# a version and would stamp the binaries "-unk". build.sh computes the real one
# on the host and passes it in here.
ARG BUILD_DESC=
ENV DRAGONX_BUILD_DESC=${BUILD_DESC}
RUN if [ -z "$DRAGONX_BUILD_DESC" ]; then \
echo "WARNING: no BUILD_DESC build-arg -- binaries will be stamped -unk." >&2; \
echo " Prefer ./build.sh --linux-compat, or pass --build-arg BUILD_DESC=..." >&2; \
fi
RUN cd /build && ./util/build.sh --disable-tests -j$(nproc)
# Strip binaries inside the container so extracted files are already small

View File

@@ -6,10 +6,34 @@
set -eu -o pipefail
VERSION="1.0.3"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RELEASE_DIR="$SCRIPT_DIR/release"
# Derive the release version from configure.ac instead of hardcoding it here.
# A stale literal names the release directories after the wrong version while the
# binaries inside report the real one: this said 1.0.3 while the tree was already
# 1.1.0, so `./build.sh --all-release` would have produced
# release/dragonx-1.0.3-<platform>/ full of binaries announcing 1.1.0.
# Mirrors configure.ac's _CLIENT_VERSION_SUFFIX m4 exactly:
# build < 25 -> beta(build+1) build < 50 -> rc(build-24)
# build == 50 -> plain release build > 50 -> point release (build-50)
_acdef() { sed -n "s/^define(_CLIENT_VERSION_$1, *\([0-9]\{1,\}\))/\1/p" "$SCRIPT_DIR/configure.ac"; }
_V_MAJOR="$(_acdef MAJOR)"
_V_MINOR="$(_acdef MINOR)"
_V_REVISION="$(_acdef REVISION)"
_V_BUILD="$(_acdef BUILD)"
if [ -z "$_V_MAJOR" ] || [ -z "$_V_MINOR" ] || [ -z "$_V_REVISION" ] || [ -z "$_V_BUILD" ]; then
echo "ERROR: could not read the version from $SCRIPT_DIR/configure.ac" >&2
echo " refusing to build a release whose directory name would be wrong." >&2
exit 1
fi
if [ "$_V_BUILD" -lt 25 ]; then _V_SUFFIX="$_V_REVISION-beta$((_V_BUILD + 1))"
elif [ "$_V_BUILD" -lt 50 ]; then _V_SUFFIX="$_V_REVISION-rc$((_V_BUILD - 24))"
elif [ "$_V_BUILD" -eq 50 ]; then _V_SUFFIX="$_V_REVISION"
else _V_SUFFIX="$_V_REVISION-$((_V_BUILD - 50))"
fi
VERSION="$_V_MAJOR.$_V_MINOR.$_V_SUFFIX"
# Parse release flags
BUILD_LINUX_RELEASE=0
BUILD_WIN_RELEASE=0
@@ -140,7 +164,34 @@ if [ $BUILD_LINUX_COMPAT -eq 1 ] || [ $BUILD_LINUX_RELEASE -eq 1 ] || [ $BUILD_W
COMPAT_RELEASE_DIR="$RELEASE_DIR/dragonx-$VERSION-$COMPAT_PLATFORM"
echo "Building Docker image (Ubuntu 20.04 base)..."
$DOCKER_CMD build -f Dockerfile.compat -t "$DOCKER_IMAGE" .
# .dockerignore excludes .git, so genbuild.sh inside the container cannot
# derive the version and would stamp the binaries "-unk". Compute it on the
# host, mirroring util/genbuild.sh exactly, and pass it in via --build-arg.
# NB: build.sh runs under `set -eu -o pipefail`, so every git call here must be
# non-fatal -- a source tarball, a machine without git, or a branch whose only
# reachable tags are lightweight (v1.0.1-v1.0.3) would otherwise abort the build.
git diff >/dev/null 2>&1 || true # refresh index: touched-but-unmodified are not dirty
COMPAT_BUILD_DESC=""
COMPAT_RAWDESC=$(git describe --abbrev=0 2>/dev/null || true)
if [ -n "$COMPAT_RAWDESC" ] \
&& [ "$(git rev-parse HEAD 2>/dev/null)" = "$(git rev-list -1 "$COMPAT_RAWDESC" 2>/dev/null)" ] \
&& git diff-index --quiet HEAD -- 2>/dev/null; then
COMPAT_BUILD_DESC="$COMPAT_RAWDESC"
else
COMPAT_SUFFIX=$(git rev-parse --short HEAD 2>/dev/null || true)
if [ -n "$COMPAT_SUFFIX" ]; then
git diff-index --quiet HEAD -- 2>/dev/null || COMPAT_SUFFIX="$COMPAT_SUFFIX-dirty"
COMPAT_BUILD_DESC="v$VERSION-$COMPAT_SUFFIX"
fi
fi
if [ -n "$COMPAT_BUILD_DESC" ]; then
echo "Stamping container build as: $COMPAT_BUILD_DESC"
else
echo "Warning: no usable git metadata; container binaries will be stamped -unk"
fi
$DOCKER_CMD build -f Dockerfile.compat \
--build-arg BUILD_DESC="$COMPAT_BUILD_DESC" \
-t "$DOCKER_IMAGE" .
echo "Extracting binaries from Docker image..."
CONTAINER_ID=$($DOCKER_CMD create "$DOCKER_IMAGE")

View File

@@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 1)
dnl Must be kept in sync with src/clientversion.h , ugh!
define(_CLIENT_VERSION_MINOR, 1)
define(_CLIENT_VERSION_MINOR, 3)
define(_CLIENT_VERSION_REVISION, 0)
define(_CLIENT_VERSION_BUILD, 50)
define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50)))
@@ -833,6 +833,22 @@ AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin])
AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin])
AM_CONDITIONAL([TARGET_LINUX], [test x$TARGET_OS = xlinux])
AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows])
dnl mingw ld is single-pass: bracket the internal static archives in a link group
dnl so it re-scans and resolves the cross-references DragonX added between them
dnl (libbitcoin_util/common objects using UniValue; util<->common mutual deps).
dnl Delivered via AC_SUBST (not an automake conditional) so automake does not
dnl reject the linker flags inside _LDADD. Empty elsewhere (macOS ld64 rejects the
dnl flag; GNU ld on Linux re-scans archives already).
if test "x$TARGET_OS" = "xwindows"; then
LINK_GROUP_START="-Wl,--start-group"
LINK_GROUP_END="-Wl,--end-group"
else
LINK_GROUP_START=""
LINK_GROUP_END=""
fi
AC_SUBST(LINK_GROUP_START)
AC_SUBST(LINK_GROUP_END)
AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
AM_CONDITIONAL([ENABLE_MINING],[test x$enable_mining = xyes])
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])

View File

@@ -1,3 +1,68 @@
dragonx (1.3.0) stable; urgency=medium
* RandomX stratum mining support: the daemon can serve stratum clients
directly, with a reference miner behind the stratummine RPC for testing.
* Stratum fixes found by audit: a malformed 63-character job_id no longer
aborts the daemon; each block is paid to the miner that actually found it
rather than to whichever client asked for work first; and a low-difficulty
share is now rejected before it costs a RandomX hash.
* Fix -connect never dialing its targets, so a node pinned to specific peers
reaches them instead of silently falling through to peer discovery.
* Remove roughly 7,100 lines of dead code, including the CBOPRET price
validation in the coinbase check, whose guard could not be true on any
chain, and adaptive-PoW difficulty logic that DragonX does not enable.
* Honor a command-line -rpcpassword across restarts, and fix a misspelled
-rpcusername key that silently discarded the configured RPC user.
* Default -checkpoints off on regtest so an isolated node leaves initial
block download, instead of staying in IBD forever and disabling every
operation gated on it.
* Release cs_main and the mempool lock on the miner's isStake error paths;
the leak presented as a permanent stall rather than a slow response.
* Windows cross-build: the mingw target links and finds librustzcash, and a
fs::path::c_str() regression in init no longer breaks the build.
* Stamp container builds with the real version instead of "-unk".
* Repair the qa/rpc-tests harness far enough to start a DragonX node and
build the shared test chain; it previously started mainnet nodes.
-- DragonX Developers <dev@dragonx.is> Mon, 31 Aug 2026 03:46:19 +0000
dragonx (1.2.0) stable; urgency=medium
* Auto-shield matured coinbase into a wallet-owned Sapling address on a block
interval. The destination is derived from the HD seed at m/32'/coin'/i' and
is the lowest index inside -mnemonicsaplinggap, so a bare seed-phrase restore
re-derives it; auto-shielding refuses to run rather than send anywhere a
restore would not find. Enabled only when the seed's provenance is known to be
recoverable, so upgraded wallets stay opted out until the operator says
otherwise.
* Create new wallets from a BIP39 seed phrase by default, byte-compatible with
SilentDragonXLite. z_exportmnemonic returns the phrase; -mnemonic restores
from it.
* New RPC z_autoshieldstatus reports whether auto-shielding is on, the resolved
destination, the HD seed's provenance, and why it is off when it is off.
* Bound each auto-shield round to 400 inputs and correct the transaction size
estimate to account for all three Sapling output descriptions, and lock the
selected coins for the duration of proof building so a concurrent
z_shieldcoinbase or z_sendmany cannot select them too.
* Repair, rather than reject, an hdchain record truncated by an older wallet
build. Previously one address generated under a pre-1.1.0 binary left the
wallet unopenable with "Wallet corrupted"; the record is now completed and
rewritten, and the error text names the seed-phrase remedy when it genuinely
cannot be recovered.
* Clear a stale sweep flag that could otherwise leave sweeping, consolidation
and auto-shielding permanently disabled together, and stop the async queue
silently discarding operations at shutdown while reporting success.
* Peer discovery: seed from the round-robin DNS record seed.dragonx.is rather
than three hostnames that no longer resolve, count DNS-seeded addresses so
the fixed-seed fallback is no longer triggered spuriously, give every entry
in the compiled-in seed list its P2P port, and stop non-DRAGONX smart chains
inheriting DragonX's seeds. Adds two seed nodes in new regions.
* Derive the release version from configure.ac in build.sh instead of a
hardcoded literal, and document container-based release builds in
doc/build-containers.md.
-- DragonX Developers <dev@dragonx.is> Tue, 25 Aug 2026 19:45:00 +0000
dragonx (1.1.0) stable; urgency=medium
* Extend DRAGONX checkpoints to height 3,226,000, enabling the existing
@@ -17,7 +82,7 @@ dragonx (1.1.0) stable; urgency=medium
pre-verification, adaptive -dbcache, Sapling witness desync fix, BIP39
seed phrases, chain-level Sapling turnstile, and the audit fixes.
-- DragonX Developers <dev@dragonx.is> Thu, 21 Aug 2026 22:00:00 +0000
-- DragonX Developers <dev@dragonx.is> Sun, 23 Aug 2026 10:41:53 -0500
dragonx (1.0.3) stable; urgency=medium
@@ -29,6 +94,22 @@ dragonx (1.0.3) stable; urgency=medium
-- DragonX <dan-s-dev@proton.me> Tue, 07 Jul 2026 05:49:59 +0200
dragonx (1.0.2) stable; urgency=medium
* Fix Sapling pool persistence, and report the block subsidy and total fees
in the getblock RPC
* Fix the Windows bootstrap script and add a mirror fallback
* Fix the Windows build
* Fix the macOS Sequoia build with GCC 15
-- DragonX <dan-s-dev@proton.me> Thu, 19 Mar 2026 10:09:18 -0500
dragonx (1.0.1) stable; urgency=medium
* Fix a fresh-sync failure at the difficulty reset height 2838976
-- DragonX <dan-s-dev@proton.me> Thu, 12 Mar 2026 01:25:21 -0500
dragonx (1.0.0) stable; urgency=medium
* Initial release of DragonX, forked from Hush Full Node
@@ -36,7 +117,7 @@ dragonx (1.0.0) stable; urgency=medium
* RandomX proof-of-work, 36-second block time, fully shielded transactions
* New binary names: dragonxd, dragonx-cli, dragonx-tx
-- DragonX <dan-s-dev@proton.me> Mon, 03 Mar 2026 00:00:00 +0000
-- DragonX <dan-s-dev@proton.me> Tue, 10 Mar 2026 19:39:55 -0500
hush (3.10.5) stable; urgency=medium

View File

@@ -1,3 +1,3 @@
DEBIAN/manpages/dragonx-cli.1
DEBIAN/manpages/dragonx-tx.1
DEBIAN/manpages/dragonxd.1
doc/man/dragonxd.1
doc/man/dragonx-cli.1
doc/man/dragonx-tx.1

View File

@@ -177,7 +177,7 @@ def main():
g.write('\n')
with open(os.path.join(indir,'nodes_test.txt'), 'r', encoding="utf8") as f:
process_nodes(g, f, 'chainparams_seed_test')
g.write('#endif // HUSH_CHAINPARAMSSEEDS_H\n')
g.write('#endif // DRAGONX_CHAINPARAMSSEEDS_H\n')
if __name__ == '__main__':
main()

View File

@@ -1,14 +1,23 @@
# generate-seeds.py expects <ip>:<port> (see its docstring). Without the port it
# emits 0, and every fixed seed becomes unconnectable -- which is what shipped:
# the whole chainparams_seed_main array carried 0x00,0x00 as the port.
# node1.dragonx.is
212.56.41.63
212.56.41.63:21768
# node2.dragonx.is
194.140.198.176
194.140.198.176:21768
# node3.dragonx.is
212.56.41.47
212.56.41.47:21768
# node4.dragonx.is
144.126.147.165
144.126.147.165:21768
# node5.dragonx.is
176.126.87.241
176.126.87.241:21768
# node6.dragonx.is
13.140.58.251:21768
# node7.dragonx.is
5.104.83.100:21768

View File

@@ -53,6 +53,6 @@ endif
define $(package)_stage_cmds
mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \
mkdir $($(package)_staging_dir)$(host_prefix)/include/ && \
cp $($(package)_library_file) $($(package)_staging_dir)$(host_prefix)/lib/ && \
cp $($(package)_library_file) $($(package)_staging_dir)$(host_prefix)/lib/librustzcash.a && \
cp librustzcash/include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/
endef

223
doc/build-containers.md Normal file
View File

@@ -0,0 +1,223 @@
# Building release binaries in containers
Release binaries must be built in a container based on an **old** Linux distribution.
This document is written to be executed, by a person or an agent, on a machine that
has nothing set up yet.
---
## 1. Why this exists
glibc compatibility runs one way only. A binary linked against glibc 2.35 demands
symbol versions that glibc 2.31 does not have, and refuses to start. A binary linked
against glibc 2.29 runs on 2.29, 2.31 and 2.35 alike.
Measured on the actual fleet, 2026-08-25:
| binary | max GLIBC required | runs on |
|---|---|---|
| what all four 20.04 seeds run today (`v1.0.3-d159e7208`) | `GLIBC_2.29` | 18.04, 20.04, 22.04 |
| anything built on seed 176 today (Ubuntu 22.04) | `GLIBC_2.34` | 22.04 only |
The second binary will not start on four of our own five seeds. The loader reports:
```
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found
/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.30' not found
```
Nothing new is being *called*. glibc 2.34 merged libpthread and libdl into libc and
re-versioned every `pthread_*`, `dlsym` and `dladdr` symbol; 2.33 replaced the old
`__xstat` inlines with real `stat`/`fstat`/`lstat64`. All of those functions exist in
2.31 under older tags. Building against older headers is the entire fix.
**Do not try to solve this with full static linking.** The daemon calls `getaddrinfo`,
`gethostbyname` and `getnameinfo`, and it must resolve `node1..node5.dragonx.is`, which
are hard-coded and injected into `-addnode` on every node. Under a fully static glibc
binary those go through NSS, which `dlopen`s `libnss_dns.so.2` at run time — it either
fails or silently requires the target to have the same glibc you linked against, which
defeats the purpose.
---
## 2. What already exists in this repo
Do not write a new build system. Two pieces are already here:
- **`Dockerfile.compat`** — an Ubuntu base image that installs the toolchain, copies the
tree, **deletes any host-built `depends/` and object files**, runs `./util/build.sh`,
and strips the three binaries.
- **`./build.sh --linux-compat`** — builds that image, creates a throwaway container,
copies `dragonxd`, `dragonx-cli` and `dragonx-tx` out into
`release/dragonx-<version>-linux-amd64-ubuntu2004/`, adds `bootstrap-dragonx.sh`,
`asmap.dat` and the two sapling params, fixes ownership, and prints the binary's
maximum required GLIBC version.
The base image is parameterised via `ARG BASE_IMAGE` (default `ubuntu:20.04`), so the
same Dockerfile can target several glibc floors.
---
## 3. Prerequisites
Docker (the scripted path uses `docker` specifically; podman works for the manual path
if you alias or substitute it).
```sh
sudo apt-get update
sudo apt-get install -y docker.io git
sudo usermod -aG docker "$USER" # then log out and back in, or every command needs sudo
```
Budget, measured on a 4-core box:
| resource | needs |
|---|---|
| disk | ~15 GB free (the `depends/` tree alone is ~1.6 GB per target, plus image layers) |
| RAM | 4 GB minimum, 8 GB comfortable — the link step is the peak |
| time | **12 hours per base image on first build.** `depends/` builds boost, BDB, wolfssl, libevent, libsodium, libcurl and rust from source. Later builds reuse Docker layer cache unless the tree changed. |
`depends/` downloads and builds its own rust toolchain, so the host's rust (or absence
of it) is irrelevant.
---
## 4. Build one target
```sh
git clone https://git.dragonx.is/DragonX/dragonx
cd dragonx
git checkout <the tag or branch you are releasing>
./build.sh --linux-compat
```
Output lands in `release/dragonx-<version>-linux-amd64-ubuntu2004/` and the script
prints the max GLIBC at the end. `<version>` is read from `configure.ac`, not
hardcoded, so it always matches what the binaries report.
---
## 5. Build several targets
```sh
for BASE in ubuntu:18.04 ubuntu:20.04 ubuntu:22.04; do
TAG="dragonx-compat-${BASE#ubuntu:}"
TAG="${TAG//./}"
docker build --build-arg "BASE_IMAGE=$BASE" -f Dockerfile.compat -t "$TAG" .
OUT="release/dragonx-$(grep -oP 'define\(_CLIENT_VERSION_MAJOR, \K[0-9]+' configure.ac).$(grep -oP 'define\(_CLIENT_VERSION_MINOR, \K[0-9]+' configure.ac).$(grep -oP 'define\(_CLIENT_VERSION_REVISION, \K[0-9]+' configure.ac)-linux-amd64-${BASE#ubuntu:}"
mkdir -p "$OUT"
CID=$(docker create "$TAG")
for b in dragonxd dragonx-cli dragonx-tx; do docker cp "$CID:/build/src/$b" "$OUT/$b"; done
docker rm "$CID" >/dev/null
cp util/bootstrap-dragonx.sh contrib/asmap/asmap.dat sapling-output.params sapling-spend.params "$OUT/" 2>/dev/null || true
done
```
### Which base to choose
| base | glibc it provides | default GCC | verdict |
|---|---|---|---|
| `ubuntu:18.04` | 2.27 | 7 | **Verify before relying on it.** The tree is built with `-std=c++17`; GCC 7's C++17 support is incomplete and its cmake (3.10) may be too old for RandomX. Attempt only if you need to reach 18.04 users, and treat a successful build as the proof. |
| `ubuntu:20.04` | 2.31 | 9 | **Recommended floor.** GCC 9 covers C++17 fully. Evidence it works: the binary the fleet runs today requires only `GLIBC_2.29`, i.e. the code touches nothing newer, so a 20.04 build reaches 18.04 machines anyway. |
| `ubuntu:22.04` | 2.35 | 11 | **Do not ship this.** It is what we already have and what excludes four of our own seeds. Useful only for development. |
Ubuntu 20.04 left standard support in April 2025, which is precisely why it belongs in
a container on a patched host rather than on a build box someone has to maintain.
---
## 6. Verify — this step is not optional
A build that silently targets the wrong glibc looks completely normal until a user
reports that nothing starts.
```sh
BIN=release/dragonx-<version>-linux-amd64-ubuntu2004/dragonxd
# The ceiling. Must be <= the glibc of the OLDEST system you intend to support.
objdump -p "$BIN" | grep -oE 'GLIBC_2\.[0-9]+' | sort -t. -k2 -n | tail -1
objdump -p "$BIN" | grep -oE 'GLIBCXX_3\.4\.[0-9]+' | sort -t. -k3 -n | tail -1
# If the ceiling is too high, this names the symbols responsible.
readelf --dyn-syms --wide "$BIN" | grep -E '@GLIBC_2\.(3[2-9])'
```
Expected for a 20.04 build: `GLIBC_2.29` or lower, `GLIBCXX_3.4.26` or lower.
Then actually run it somewhere old. A ceiling check proves the loader will resolve the
symbols; it does not prove the binary works. `./dragonxd --version` on a real 20.04 box
is a ten-second confirmation.
---
## 7. Traps
Each of these has cost real time.
**`ETXTBSY` when installing over a running daemon.** `cp` onto the binary fails with
"Text file busy" *even after the process has exited*`pgrep` returning nothing is not
sufficient, the kernel still holds the text mapping. Stage into the same directory and
`mv` (rename is not blocked), allow ~10 s to settle, and **sha256-verify the installed
file before starting it**. A failed copy that goes unnoticed leaves the old binary
running and looks like a successful deploy.
**Never touch `configure.ac` in a configured tree.** Even `cp`-ing back a byte-identical
copy updates its mtime, which makes `make` regenerate `aclocal.m4` and `configure` and
then re-run `configure`, which fails with `libdb_cxx headers missing` because the
depends prefix is not on the command line. If it happens: confirm
`git diff --quiet HEAD -- configure.ac`, then restore mtime order oldest-to-newest with
one-second gaps — `configure.ac`/`Makefile.am`, then `aclocal.m4`, then
`configure`/`Makefile.in`, then `config.status`, then `Makefile`. Inside a container
this cannot happen, which is one more reason to build there.
**Never blind-`touch` a path that might not exist.** `touch src/config/hush-config.h`
silently *creates* an empty stray file; the real header is `bitcoin-config.h`. Check
`git status` after any timestamp surgery.
**RandomX must be built with `ARCH=default`.** `util/build.sh` already passes it and the
comment there explains why: `ARCH=native` tunes to the build machine, and a build on an
AVX-512 host emitted 746 `zmm` instructions into `librandomx.a`, which `SIGILL`s on the
entire fleet. If you ever invoke cmake by hand, pass `-DARCH=default`.
**Strip before distributing.** Unstripped is ~220 MB, stripped ~16 MB. `Dockerfile.compat`
already strips inside the container.
**`util/build-win.sh` discards every argument.** There is no `"$@"` handling in it, so
`-j$(nproc)` and `--disable-tests` are dropped on the floor and the Windows build is
single-threaded. Expect it to be far slower than you planned.
**Windows also needs `-Wa,-mbig-obj` and `-DARCH=default`.** Both are in
`util/build-win.sh` today. The mingw flag was missing from `dev` for a month; without it
the cross-compile fails at link because boost-heavy translation units exceed the
PE/COFF section limit. Do not lose it on a re-branch.
**macOS cannot be containerised.** `util/build-mac.sh` is a native-Mac script, and there
is no darwin cross-compile path in `depends/` at all: `hosts/darwin.mk` wants
`native_cctools`, which has no package definition, and there is no SDK in the tree. It
also hardcodes an Intel Homebrew GCC path, so it produces x86_64 only — no arm64, no
universal binary. macOS needs a real Mac.
**The `contrib/gitian-descriptors/` files are not a build path.** They are unmodified
upstream Bitcoin files (`name: "bitcoin-win-0.11"`, suite `trusty`) with zero DragonX
content. Ignore them.
---
## 8. Handoff checklist
- [ ] Docker installed, user in the `docker` group, ~15 GB free
- [ ] Correct tag or branch checked out, tree clean (`git status`)
- [ ] Version in `configure.ac` is the one you intend to release
- [ ] `./build.sh --linux-compat` completes
- [ ] GLIBC ceiling is **2.31 or lower** (2.29 expected)
- [ ] GLIBCXX ceiling is **3.4.28 or lower** (3.4.26 expected)
- [ ] `dragonxd --version` runs on a real machine of the oldest supported distro
- [ ] Binaries stripped, `release/` contains the bootstrap script, `asmap.dat` and both sapling params
- [ ] sha256 recorded for each artifact
One more thing that is not a build step but belongs in the same conversation: the
in-app daemon updater refuses any release without a detached signature
(`kDaemonRequireSignature = true`). Publishing checksums alone means no existing user
can update in place.

View File

@@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
.TH DRAGONX "1" "July 2026" "DragonX RPC client version v1.0.3-95aeaed0c-dirty" "User Commands"
.TH DRAGONX-CLI "1" "August 2026" "dragonx-cli v1.3.0" "User Commands"
.SH NAME
DragonX \- manual page for DragonX RPC client version v1.0.3-95aeaed0c-dirty
dragonx-cli \- manual page for dragonx-cli v1.3.0
.SH DESCRIPTION
DragonX RPC client version v1.0.3\-95aeaed0c\-dirty
DragonX RPC client version v1.3.0\-af7d9e230
.PP
In order to ensure you are adequately protecting your privacy when using
DragonX, please see <https://dragonx.is/security/>.
@@ -70,25 +70,22 @@ Timeout in seconds during HTTP requests, or 0 for no timeout. (default:
.IP
Read extra arguments from standard input, one per line until EOF/Ctrl\-D
(recommended for sensitive information such as passphrases)
.PP
.SH COPYRIGHT
In order to ensure you are adequately protecting your privacy when using
DragonX, please see <https://dragonx.is/security/>.
.SH COPYRIGHT
Copyright \(co 2024\-2026 The DragonX Developers
.PP
.br
Copyright \(co 2016\-2024 Duke Leto and The Hush Developers
.PP
.br
Copyright \(co 2016\-2020 jl777 and SuperNET developers
.PP
.br
Copyright \(co 2016\-2018 The Zcash developers
.PP
.br
Copyright \(co 2009\-2014 The Bitcoin Core developers
.PP
Copyright (C) 2024-2026 The DragonX Developers
Copyright (C) 2016-2024 Duke Leto and The Hush Developers
Copyright (C) 2016-2020 jl777 and SuperNET developers
Copyright (C) 2016-2018 The Zcash developers
Copyright (C) 2009-2014 The Bitcoin Core developers
This is experimental Free Software! Fuck Yeah!!!!!
.PP
Distributed under the GPLv3 software license, see the accompanying file COPYING
or <https://www.gnu.org/licenses/gpl\-3.0.en.html>.
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.

View File

@@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
.TH DRAGONX-TX "1" "July 2026" "dragonx-tx v1.0.3-4caf2fc68" "User Commands"
.TH DRAGONX-TX "1" "August 2026" "dragonx-tx v1.3.0" "User Commands"
.SH NAME
dragonx-tx \- DragonX transaction utility
dragonx-tx \- manual page for dragonx-tx v1.3.0
.SH DESCRIPTION
hush\-tx utility version v1.0.3\-4caf2fc68
hush\-tx utility version v1.3.0\-af7d9e230
.SS "Usage:"
.TP
hush\-tx [options] <hex\-tx> [commands]
@@ -84,3 +84,22 @@ Load JSON file FILENAME into register NAME
set=NAME:JSON\-STRING
.IP
Set register NAME to given JSON\-STRING
.SH COPYRIGHT
In order to ensure you are adequately protecting your privacy when using
DragonX, please see <https://dragonx.is/security/>.
Copyright (C) 2024-2026 The DragonX Developers
Copyright (C) 2016-2024 Duke Leto and The Hush Developers
Copyright (C) 2016-2020 jl777 and SuperNET developers
Copyright (C) 2016-2018 The Zcash developers
Copyright (C) 2009-2014 The Bitcoin Core developers
This is experimental Free Software! Fuck Yeah!!!!!
Distributed under the GPLv3 software license, see the accompanying file COPYING
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.

View File

@@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
.TH DRAGONX "1" "July 2026" "DragonX Daemon version v1.0.3-4caf2fc68" "User Commands"
.TH DRAGONXD "1" "August 2026" "dragonxd v1.3.0" "User Commands"
.SH NAME
DragonX \- manual page for DragonX Daemon version v1.0.3-4caf2fc68
dragonxd \- manual page for dragonxd v1.3.0
.SH DESCRIPTION
DragonX Daemon version v1.0.3\-4caf2fc68
DragonX Daemon version v1.3.0\-af7d9e230
.PP
In order to ensure you are adequately protecting your privacy when using
DragonX, please see <https://dragonx.is/security/>.
@@ -52,7 +52,7 @@ Specify directory to be used when exporting data
.HP
\fB\-dbcache=\fR<n>
.IP
Set database cache size in megabytes (4 to 16384). Default: adaptive \-
Set database cache size in megabytes (4 to 65536). Default: adaptive \-
uses most free RAM to speed up initial block download (far fewer
UTXO flushes to disk) and automatically shrinks if other
applications need memory, always leaving a reserve free. Setting
@@ -88,8 +88,8 @@ leave that many cores free, default: 0)
\fB\-randomxverifythreads=\fR<n>
.IP
Number of threads for parallel RandomX PoW pre\-verification of
post\-checkpoint blocks during sync (0 = inline only, max 16,
default: same as \fB\-par\fR)
post\-checkpoint blocks during network sync; no effect on reindex
(0 = inline only, max 16, default: same as \fB\-par\fR)
.HP
\fB\-pid=\fR<file>
.IP
@@ -361,15 +361,18 @@ exposes the seed to your shell history and process list.
\fB\-mnemonic=\fR<words>
.IP
Restore/create a fresh/empty wallet from a BIP39 seed phrase, compatible
with SilentDragonXLite (English, no passphrase). WARNING: exposes
the phrase to your shell history and process list; prefer
DRAGONX.conf with tight permissions.
with SilentDragonXLite (English, no passphrase; cross\-wallet
restore parity is mainnet\-only \fB\-\-\fR testnet/regtest derive a
different HD coin_type). WARNING: exposes the phrase to your
shell history and process list; prefer DRAGONX.conf with tight
permissions.
.HP
\fB\-usemnemonic\fR
.IP
Create new wallets from a fresh BIP39 seed phrase so the 24 words can be
exported (z_exportmnemonic) and used in SilentDragonXLite
(default: 0)
exported (z_exportmnemonic) and used in SilentDragonXLite. Set to
0 for a raw random seed with no recovery phrase; existing wallets
are never changed (default: 1)
.HP
\fB\-hdtransparentgaplimit=\fR<n>
.IP
@@ -427,6 +430,40 @@ Enable sweeping to an external wallet (default false)
.IP
Addresses to exclude from sweeping (default none)
.HP
\fB\-autoshield\fR
.IP
Automatically shield matured coinbase (mining rewards) into a
seed\-derived wallet z\-address (default: true for wallets created
or restored by this software, false when the HD seed provenance
is unknown). No\-op when not mining or wallet is locked.
.HP
\fB\-autoshieldinterval\fR
.IP
Block interval between automatic coinbase\-shielding rounds (default: 25,
min 5)
.HP
\fB\-autoshieldaddress=\fR<zaddr>
.IP
Destination Sapling z\-address for auto\-shielded coinbase (default: reuse
or create a wallet z\-address). Must be spendable by this wallet.
.HP
\fB\-autoshieldfee\fR
.IP
Fee in puposhis for automatic coinbase\-shielding transactions (default:
10000)
.HP
\fB\-sietch\-min\-zouts=\fR<n>
.IP
Minimum number of shielded (Sapling) outputs Sietch adds to each
z_sendmany transaction as decoys, strengthening
amount/linkability privacy. Higher values add privacy at the cost
of larger transactions (default: 7, clamped to the range 3\-50)
.HP
\fB\-autoshieldminutxos\fR
.IP
Only auto\-shield once at least this many matured coinbase UTXOs exist
(default: 1)
.HP
\fB\-deletetx\fR
.IP
Enable Old Transaction Deletion
@@ -446,7 +483,7 @@ Keep transactions for at least <n> blocks (default: 10000)
.HP
\fB\-paytxfee=\fR<amt>
.IP
Fee (in HUSH/kB) to add to transactions you send (default: 0.00)
Fee (in DRAGONX/kB) to add to transactions you send (default: 0.00)
.HP
\fB\-keepnotewitnesscache\fR
.IP
@@ -485,7 +522,7 @@ mined will become invalid (default: 200)
.HP
\fB\-maxtxfee=\fR<amt>
.IP
Maximum total fees (in HUSH) to use in a single wallet transaction;
Maximum total fees (in DRAGONX) to use in a single wallet transaction;
setting this too low may abort large transactions (default: 0.10)
.HP
\fB\-upgradewallet\fR
@@ -554,8 +591,8 @@ Prepend debug output with timestamp (default: 1)
.HP
\fB\-minrelaytxfee=\fR<amt>
.IP
Fees (in HUSH/kB) smaller than this are considered zero fee for relaying
(default: 0.000001)
Fees (in DRAGONX/kB) smaller than this are considered zero fee for
relaying (default: 0.000001)
.HP
\fB\-printtoconsole\fR
.IP
@@ -680,6 +717,11 @@ Stratum server options:
.IP
Enable stratum server (default: off)
.HP
\fB\-stratumtarget=\fR<hex>
.IP
Pool share target (64\-hex, big\-endian; larger = easier). Default is the
diff\-1 target. Useful for solo/low\-difficulty mining.
.HP
\fB\-stratumaddress=\fR<address>
.IP
Mining address to use when special address of 'x' is sent by miner
@@ -804,25 +846,22 @@ Starting supply, default is 10
\fB\-ac_txpow\fR
.IP
Enforce transaction\-rate limit, default 0
.PP
.SH COPYRIGHT
In order to ensure you are adequately protecting your privacy when using
DragonX, please see <https://dragonx.is/security/>.
.SH COPYRIGHT
Copyright \(co 2024\-2026 The DragonX Developers
.PP
.br
Copyright \(co 2016\-2024 Duke Leto and The Hush Developers
.PP
.br
Copyright \(co 2016\-2020 jl777 and SuperNET developers
.PP
.br
Copyright \(co 2016\-2018 The Zcash developers
.PP
.br
Copyright \(co 2009\-2014 The Bitcoin Core developers
.PP
Copyright (C) 2024-2026 The DragonX Developers
Copyright (C) 2016-2024 Duke Leto and The Hush Developers
Copyright (C) 2016-2020 jl777 and SuperNET developers
Copyright (C) 2016-2018 The Zcash developers
Copyright (C) 2009-2014 The Bitcoin Core developers
This is experimental Free Software! Fuck Yeah!!!!!
.PP
Distributed under the GPLv3 software license, see the accompanying file COPYING
or <https://www.gnu.org/licenses/gpl\-3.0.en.html>.
or <https://www.gnu.org/licenses/gpl-3.0.en.html>.

View File

@@ -1,296 +0,0 @@
<!-- Creator : groff version 1.23.0 -->
<!-- CreationDate: Mon Jan 5 14:12:33 2026 -->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta name="generator" content="groff -Thtml, see www.gnu.org">
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<meta name="Content-Style" content="text/css">
<style type="text/css">
p { margin-top: 0; margin-bottom: 0; vertical-align: top }
pre { margin-top: 0; margin-bottom: 0; vertical-align: top }
table { margin-top: 0; margin-bottom: 0; vertical-align: top }
h1 { text-align: center }
</style>
<title>HUSH-CLI</title>
</head>
<body>
<h1 align="center">HUSH-CLI</h1>
<a href="#NAME">NAME</a><br>
<a href="#DESCRIPTION">DESCRIPTION</a><br>
<a href="#Usage:">Usage:</a><br>
<a href="#OPTIONS">OPTIONS</a><br>
<a href="#COPYRIGHT">COPYRIGHT</a><br>
<hr>
<h2>NAME
<a name="NAME"></a>
</h2>
<p style="margin-left:9%; margin-top: 1em">hush-cli -
manual page for hush-cli v3.10.4</p>
<h2>DESCRIPTION
<a name="DESCRIPTION"></a>
</h2>
<p style="margin-left:9%; margin-top: 1em">Hush RPC client
version v3.10.4-7e63e2f01-dirty</p>
<p style="margin-left:9%; margin-top: 1em">In order to
ensure you are adequately protecting your privacy when using
Hush, please see &lt;https://hush.is/security/&gt;.</p>
<h3>Usage:
<a name="Usage:"></a>
</h3>
<p style="margin-left:9%; margin-top: 1em">hush-cli
[options] &lt;command&gt; [params]</p>
<p style="margin-left:18%;">Send command to Hush</p>
<p style="margin-left:9%;">hush-cli [options] help</p>
<p style="margin-left:18%;">List commands</p>
<p style="margin-left:9%;">hush-cli [options] help
&lt;command&gt;</p>
<p style="margin-left:18%;">Get help for a command</p>
<h2>OPTIONS
<a name="OPTIONS"></a>
</h2>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em">-?</p></td>
<td width="88%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">This help
message</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="15%">
<p style="margin-top: 1em"><b>-conf=</b>&lt;file&gt;</p></td>
<td width="76%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Specify
configuration file (default: HUSH3.conf)</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="18%">
<p style="margin-top: 1em"><b>-datadir=</b>&lt;dir&gt;</p></td>
<td width="73%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Specify data
directory (this path cannot use &rsquo;&#732;&rsquo;)</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="10%">
<p style="margin-top: 1em"><b>-testnet</b></p></td>
<td width="81%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Use the test
network</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="10%">
<p style="margin-top: 1em"><b>-regtest</b></p></td>
<td width="81%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Enter
regression test mode, which uses a special chain in which
blocks can be solved instantly. This is intended for
regression testing tools and app development.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="20%">
<p style="margin-top: 1em"><b>-rpcconnect=</b>&lt;ip&gt;</p> </td>
<td width="71%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Send commands
to node running on &lt;ip&gt; (default: 127.0.0.1)</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="19%">
<p style="margin-top: 1em"><b>-rpcport=</b>&lt;port&gt;</p> </td>
<td width="72%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Connect to
JSON-RPC on &lt;port&gt; (default: 18030 )</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="10%">
<p style="margin-top: 1em"><b>-rpcwait</b></p></td>
<td width="81%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Wait for RPC
server to start</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="19%">
<p style="margin-top: 1em"><b>-rpcuser=</b>&lt;user&gt;</p> </td>
<td width="72%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Username for
JSON-RPC connections</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="22%">
<p style="margin-top: 1em"><b>-rpcpassword=</b>&lt;pw&gt;</p> </td>
<td width="69%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Password for
JSON-RPC connections</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="27%">
<p style="margin-top: 1em"><b>-rpcclienttimeout=</b>&lt;n&gt;</p> </td>
<td width="64%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Timeout in
seconds during HTTP requests, or 0 for no timeout. (default:
900)</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="9%"></td>
<td width="8%">
<p style="margin-top: 1em"><b>-stdin</b></p></td>
<td width="83%">
</td></tr>
</table>
<p style="margin-left:18%; margin-top: 1em">Read extra
arguments from standard input, one per line until EOF/Ctrl-D
(recommended for sensitive information such as
passphrases)</p>
<h2>COPYRIGHT
<a name="COPYRIGHT"></a>
</h2>
<p style="margin-left:9%; margin-top: 1em">In order to
ensure you are adequately protecting your privacy when using
Hush, please see &lt;https://hush.is/security/&gt;.</p>
<p style="margin-left:9%; margin-top: 1em">Copyright (C)
2016-2025 Duke Leto and The Hush Developers</p>
<p style="margin-left:9%; margin-top: 1em">Copyright (C)
2016-2020 jl777 and SuperNET developers</p>
<p style="margin-left:9%; margin-top: 1em">Copyright (C)
2016-2018 The Zcash developers</p>
<p style="margin-left:9%; margin-top: 1em">Copyright (C)
2009-2014 The Bitcoin Core developers</p>
<p style="margin-left:9%; margin-top: 1em">This is
experimental Free Software! Fuck Yeah!!!!!</p>
<p style="margin-left:9%; margin-top: 1em">Distributed
under the GPLv3 software license, see the accompanying file
COPYING or
&lt;https://www.gnu.org/licenses/gpl-3.0.en.html&gt;.</p>
<hr>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -8,32 +8,32 @@ It is best to keep doc/relnotes/README.md up to date as changes and bug fixes ar
## Branch model
Development happens on the `dev` branch. Releases are cut on the default branch, `dragonx`. There is no `master` branch. Code changes should land on `dev` first and undergo testing before being merged into `dragonx`.
Development happens on the `dev` branch. Releases are cut on the default branch, `master`. Code changes should land on `dev` first and undergo testing before being merged into `master`.
## Check for changes on dragonx that should be on dev
## Check for changes on master that should be on dev
Occasionally trivial changes are made directly on the `dragonx` branch, such as documentation changes. In theory, no code changes should happen on `dragonx` without being on `dev` first, but it's better to be safe than sorry. We want the `dev` branch which undergoes testing to be as close as possible to what the `dragonx` branch will become, so we don't want to merge `dev` into `dragonx` and just assume everything works. So it's best to merge the `dragonx` branch into `dev` just before merging the `dev` branch into `dragonx`.
Occasionally trivial changes are made directly on the `master` branch, such as documentation changes. In theory, no code changes should happen on `master` without being on `dev` first, but it's better to be safe than sorry. We want the `dev` branch which undergoes testing to be as close as possible to what the `master` branch will become, so we don't want to merge `dev` into `master` and just assume everything works. So it's best to merge the `master` branch into `dev` just before merging the `dev` branch into `master`.
To check if the `dragonx` branch has any changes that the `dev` branch does not:
To check if the `master` branch has any changes that the `dev` branch does not:
```
# this assumes you are working with https://git.dragonx.is/DragonX/dragonx as your remote
git checkout dev
git pull # make sure dev is up to date
git checkout dragonx
git pull # make sure dragonx is up to date
git diff dev...dragonx # look at the set of changes which exist in dragonx but not dev
git checkout master
git pull # make sure master is up to date
git diff dev...master # look at the set of changes which exist in master but not dev
```
If the last command has no output, congrats, there is nothing to do. If the last command has output, then you should merge `dragonx` into `dev`:
If the last command has no output, congrats, there is nothing to do. If the last command has output, then you should merge `master` into `dev`:
```
git checkout dev
git merge dragonx
git merge master
git push origin dev
```
Use the `--no-ff` flag when merging `dev` into `dragonx` for a release (see below). The `--no-ff` flag makes sure to make a merge commit, no matter what, even if a "fast forward" could be done. For those in the future looking back, it's much better to see evidence of when branches were merged.
Use the `--no-ff` flag when merging `dev` into `master` for a release (see below). The `--no-ff` flag makes sure to make a merge commit, no matter what, even if a "fast forward" could be done. For those in the future looking back, it's much better to see evidence of when branches were merged.
### Git Issues
@@ -66,6 +66,7 @@ Install deps on Linux:
- Run "make seeds"
- Commit the result
- Update version in configure.ac and src/clientversion.h to update the dragonxd version
- **The new version MUST be higher than every version already tagged**, including tags that were never built or published. Check with `git tag -l --sort=-v:refname | head`. Two trees stamped with the same `CLIENT_VERSION` are indistinguishable on the wire, in `getnetworkinfo`, and to the wallet's in-app updater — and a published archive that does not match its tag destroys the only provenance check users have.
- In src/clientversion.h you update `CLIENT_VERSION_*` variables. Usually you will just update `CLIENT_VERSION_REVISION`
- If there is a consensus change, it may be a good idea to update `CLIENT_VERSION_MINOR` or `CLIENT_VERSION_MAJOR`
- To make a pre-release "beta" you can modify `CLIENT_VERSION_BUILD` but that is rarely done.
@@ -97,17 +98,27 @@ Install deps on Linux:
- Try to generate checkpoints as close to the release as possible, so you can have a recent block height be protected.
- For instance, don't update checkpoints and then do a release a month later. You can always update checkpoint data again or multiple times
- Update doc/relnotes/README.md
- To get the stats of file changes: `git diff --stat dragonx...dev`
- To get the stats of file changes: `git diff --stat master...dev`
- Do a fresh clone and fresh sync with new checkpoints
- Stop node, wait 20 minutes, and then do a partial sync with new checkpoints
- Merge dev into dragonx: `git checkout dev && git pull && git checkout dragonx && git pull && git merge --no-ff dev && git push`
- Merge dev into master: `git checkout dev && git pull && git checkout master && git pull && git merge --no-ff dev && git push`
- The above command makes sure that your local dev branch is up to date before doing anything
- The above command will not merge if "git pull" creates a merge conflict
- The above command will not push if there is a problem with merging dev
- Make Gitea release with git tag from the dragonx branch (make sure to merge dev in first)
- Make Gitea release with git tag from the master branch (make sure to merge dev in first)
- Make sure git tag starts with a `v` such as `v1.0.3`
- Use util/gen-linux-binary-release.sh to make a Linux release binary
- Upload Linux binary to Gitea release and add SHA256 sum
- **The tag MUST be annotated** (`git tag -a v1.3.0 -m 'DragonX v1.3.0'`), not lightweight. `util/genbuild.sh` calls `git describe` *without* `--tags`, which only ever sees annotated tags — a lightweight tag makes the build stamp itself `v<older-tag>-<sha>` instead of the release version. v1.0.0 through v1.0.3 are lightweight, which is why their builds are labelled that way.
- Verify before building: `git describe` must print exactly the tag, with no `-<n>-g<sha>` suffix.
- Use `./build.sh` (container-based, see doc/build-containers.md) or util/gen-linux-binary-release.sh to make a Linux release binary
- **Sign every archive and publish the signatures.** This step is mandatory and was missing from this document until v1.3.0 — its absence is why v1.1.0 and v1.2.0 were tagged but never became installable releases.
- The wallet's in-app daemon updater pins an ed25519 public key in `ObsidianDragon/src/util/daemon_updater.h` and sets `kDaemonRequireSignature = true`. **An update is refused outright unless a valid `<archive>.sig` is published beside the archive.** No signature means every existing user silently stays on their old daemon.
- Sign with `ObsidianDragon/scripts/sign-daemon-release.sh`:
- `scripts/sign-daemon-release.sh sign <secret.key> <archive>...` produces `<archive>.sig` (base64 of a detached 64-byte ed25519 signature over the exact archive bytes)
- or `scripts/sign-daemon-release.sh release <secret.key> <version>` to zip, sign, and print the checksum table in one step
- Keep the secret key offline, mode 600. The matching base64 public key must already be pinned in `kDaemonSignaturePublicKeyBase64`.
- Upload each Linux binary archive **and its `.sig`** to the Gitea release
- **Paste the SHA-256 checksum table into the release body** as markdown rows of the form `| <archive>.zip | `<sha256hex>` |`. The updater parses this table and will not install an archive that is absent from it.
- Confirm the release is actually consumable before announcing it: the updater looks for an asset whose name contains `"-" + platformToken + ".zip"` (`linux-amd64`, `macos`, `win64`). An archive named for a distro variant instead of the platform token is invisible to it.
- Create an x86 Debian package for the release:
- Edit contrib/debian/changelog to add information about the new release
- Use `util/build-debian-package.sh` to make an x86 Debian package for the release
@@ -115,7 +126,7 @@ Install deps on Linux:
- `lintian` is an optional dependency, it's not needed to build the .deb
- Upload .deb to Gitea release
- Add SHA256 checksum of .deb to release
- Use util/build-debian-package-ARM.sh (does this still work?) to make an ARM Debian package for the release
- ARM Debian package: `util/build-debian-package-ARM.sh` is referenced here historically but **is not present in the tree**. Skip, or restore the script first.
- Upload the debian packages to the Gitea release page, with SHA256 sums
## Platform-specific notes

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2016 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2014 The Bitcoin Core developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -52,7 +52,7 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
'''
def run_test(self):
print "Warning: this test will take about 70 seconds in the best case. Be patient."
print("Warning: this test will take about 70 seconds in the best case. Be patient.")
self.nodes[0].generate(10)
templat = self.nodes[0].getblocktemplate()
longpollid = templat['longpollid']

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2014 The Bitcoin Core developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -6,6 +6,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import initialize_chain_clean, start_node
from binascii import a2b_hex, b2a_hex
from hashlib import sha256
@@ -69,14 +70,43 @@ def genmrklroot(leaflist):
cur = n
return cur[0]
# ---------------------------------------------------------------------------
# Sapling v4 transaction layout.
#
# This test was written against the pre-Overwinter serialization, where a tx
# began with a 4-byte nVersion immediately followed by the vin count, so the
# first input's prevout hash lived at offset 4+1. DragonX transactions are
# Sapling v4: 4-byte header (nVersion | fOverwintered) + 4-byte nVersionGroupId
# + vin count, so the prevout hash starts 4 bytes further in. Poking the old
# offset corrupts nVersionGroupId and every proposal below just comes back
# "Block decode failed" instead of exercising any consensus rule.
CB_PREVOUT_OFF = 4+4+1
# Likewise the tx no longer ends at nLockTime: nExpiryHeight (4), valueBalance
# (8) and the empty vShieldedSpend/vShieldedOutput/vJoinSplit counts (1 each)
# trail it, so nLockTime is the 4 bytes at [-19:-15].
TX_TAIL_AFTER_LOCKTIME = 4+8+1+1+1
def tx_seq_off(tx):
"""Offset of the first input's nSequence in a Sapling v4 tx."""
scriptlen_off = CB_PREVOUT_OFF + 32 + 4 # after prevout hash + prevout.n
return scriptlen_off + 1 + tx[scriptlen_off]
def tx_vout0_value_off(tx):
"""Offset of the first output's 8-byte value in a Sapling v4 tx."""
return tx_seq_off(tx) + 4 + 1 # after nSequence + vout count
def template_to_bytes(tmpl, txlist):
blkver = pack('<L', tmpl['version'])
mrklroot = genmrklroot(list(dblsha(a) for a in txlist))
reserved = b'\0'*32
# hashFinalSaplingRoot. The all-zeroes placeholder this test used predates
# Sapling; a header carrying the wrong root is rejected with
# 'bad-sapling-root-in-block', which would sink even Test 11 (valid block).
reserved = a2b_hex(tmpl['finalsaplingroothash'])[::-1]
timestamp = pack('<L', tmpl['curtime'])
nonce = b'\0'*32
soln = b'\0'
blk = blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + reserved + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce + soln
# bytearray, not bytes: Test 9 mutates one byte of the result in place.
blk = bytearray(blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + reserved + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce + soln)
blk += varlenEncode(len(txlist))
for tx in txlist:
blk += tx
@@ -95,6 +125,20 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
Test block proposals with getblocktemplate.
'''
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 1)
def setup_network(self, split=False):
# -daaforkheight=0: ContextualCheckBlockHeader only enforces nBits for a
# smart chain above daaForkHeight, which defaults to
# ASSETCHAINS_RANDOMX_VALIDATION+62000 (millions of blocks) so that a
# fresh sync accepts DragonX's historical bad-nBits window. Without this
# flag Test 8 (bad bits) is unreachable at regtest heights and the
# daemon happily accepts a block with arbitrary nBits.
self.nodes = [ start_node(0, self.options.tmpdir, ['-daaforkheight=0']) ]
self.is_network_split = False
def run_test(self):
node = self.nodes[0]
node.generate(1) # Mine a block to leave initial block download
@@ -117,9 +161,9 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
#txlist[0][4+1+36+1+1] -= 1
# Test 2: Bad input hash for gen tx
txlist[0][4+1] += 1
txlist[0][CB_PREVOUT_OFF] += 1
assert_template(node, tmpl, txlist, 'bad-cb-missing')
txlist[0][4+1] -= 1
txlist[0][CB_PREVOUT_OFF] -= 1
# Test 3: Truncated final tx
lastbyte = txlist[-1].pop()
@@ -136,14 +180,33 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
# Test 5: Add an invalid tx to the end (non-duplicate)
txlist.append(bytearray(txlist[0]))
txlist[-1][4+1] = b'\xff'
txlist[-1][CB_PREVOUT_OFF] = 0xff
# DragonX is a fully private chain (ASSETCHAINS_PRIVATE): a non-coinbase
# tx paying a positive amount to a t-addr is killed in CheckTransaction
# with 'bad-txns-acprivacy-chain' before ConnectBlock ever looks up its
# inputs. Zero the output value -- CheckTransaction exempts zero-value
# vouts -- so the tx survives to the missing-input check this case is
# actually about.
_val_off = tx_vout0_value_off(txlist[-1])
txlist[-1][_val_off:_val_off+8] = b'\0'*8
assert_template(node, tmpl, txlist, 'bad-txns-inputs-missingorspent')
txlist.pop()
# Test 6: Future tx lock time
txlist[0][-4:] = b'\xff\xff\xff\xff'
# The server-supplied coinbase carries nSequence=0xffffffff, and
# IsFinalTx() short-circuits on all-final inputs, so a future nLockTime
# alone leaves the tx final and the block valid. (DragonX's IsFinalTx
# also whitelists 0xfffffffe below the Hush hardfork height, which
# regtest is.) Make the input genuinely non-final so the future
# nLockTime is the thing under test.
seq_off = tx_seq_off(txlist[0])
realseq = txlist[0][seq_off:seq_off+4]
txlist[0][seq_off:seq_off+4] = b'\0\0\0\0'
reallocktime = txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME]
txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME] = b'\xff\xff\xff\xff'
assert_template(node, tmpl, txlist, 'bad-txns-nonfinal')
txlist[0][-4:] = b'\0\0\0\0'
txlist[0][-TX_TAIL_AFTER_LOCKTIME-4:-TX_TAIL_AFTER_LOCKTIME] = reallocktime
txlist[0][seq_off:seq_off+4] = realseq
# Test 7: Bad tx count
txlist.append(b'')
@@ -171,7 +234,9 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
tmpl['curtime'] = 0x7fffffff
assert_template(node, tmpl, txlist, 'time-too-new')
tmpl['curtime'] = 0
assert_template(node, tmpl, txlist, 'time-too-old')
# DragonX split Bitcoin's 'time-too-old' into 'time-too-old-median'
# (block time <= prev MedianTimePast) and 'time-too-old-prevblock'.
assert_template(node, tmpl, txlist, 'time-too-old-median')
tmpl['curtime'] = realtime
# Test 11: Valid block

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2014 The Bitcoin Core developers
# Copyright (c) 2016-2024 The Hush developers
# Released under the GPLv3
@@ -28,7 +28,7 @@ class ReindexTest(BitcoinTestFramework):
wait_bitcoinds()
self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug", "-reindex", "-checkblockindex=1"])
assert_equal(self.nodes[0].getblockcount(), 3)
print "Success"
print("Success")
if __name__ == '__main__':
ReindexTest().main()

View File

@@ -7,7 +7,7 @@
# and for constructing a getheaders message
#
from mininode import CBlock, CBlockHeader, CBlockLocator, CTransaction, msg_block, msg_headers, msg_tx
from .mininode import CBlock, CBlockHeader, CBlockLocator, CTransaction, msg_block, msg_headers, msg_tx
import sys
import cStringIO

View File

@@ -3,8 +3,8 @@
# Distributed under the GPLv3 software license, see the accompanying
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
# blocktools.py - utilities for manipulating blocks and transactions
from mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint
from script import CScript, OP_0, OP_EQUAL, OP_HASH160
from .mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint
from .script import CScript, OP_0, OP_EQUAL, OP_HASH160
# Create a block (with regtest difficulty)
def create_block(hashprev, coinbase, nTime=None, nBits=None):

View File

@@ -3,10 +3,10 @@
# Distributed under the GPLv3 software license, see the accompanying
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
from mininode import CBlock, CTransaction, CInv, NodeConn, NodeConnCB, \
from .mininode import CBlock, CTransaction, CInv, NodeConn, NodeConnCB, \
msg_inv, msg_getheaders, msg_ping, msg_mempool, mininode_lock, MAX_INV_SZ
from blockstore import BlockStore, TxStore
from util import p2p_port
from .blockstore import BlockStore, TxStore
from .util import p2p_port
import time

View File

@@ -11,8 +11,8 @@ import shutil
import tempfile
import traceback
from authproxy import JSONRPCException
from util import assert_equal, check_json_precision, \
from .authproxy import JSONRPCException
from .util import assert_equal, check_json_precision, \
initialize_chain, initialize_chain_clean, \
start_nodes, connect_nodes_bi, stop_nodes, \
sync_blocks, sync_mempools, wait_bitcoinds
@@ -91,7 +91,7 @@ class BitcoinTestFramework(object):
parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true",
help="Don't stop nodes after the test execution")
parser.add_option("--srcdir", dest="srcdir", default="../../src",
help="Source directory containing hushd/hush-cli (default: %default)")
help="Source directory containing dragonxd/dragonx-cli (default: %default)")
parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
help="Root directory for datadirs")
parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true",

View File

@@ -18,7 +18,7 @@ import subprocess
import time
import re
from authproxy import AuthServiceProxy
from .authproxy import AuthServiceProxy
def p2p_port(n):
return 11000 + n + os.getpid()%999
@@ -97,8 +97,8 @@ def initialize_datadir(dirname, n):
print("Creating dirs %s" % datadir)
os.makedirs(datadir)
print("Writing to " + os.path.join(datadir,"ZZZ.conf"))
with open(os.path.join(datadir, "ZZZ.conf"), 'w') as f:
print("Writing to " + os.path.join(datadir,"DRAGONX.conf"))
with open(os.path.join(datadir, "DRAGONX.conf"), 'w') as f:
f.write("regtest=1\n");
f.write("txindex=1\n");
#f.write("testnode=1\n");
@@ -116,7 +116,19 @@ def initialize_datadir(dirname, n):
f.write("spentindex=1\n");
f.write("timestampindex=1\n");
#f.write("zindex=1\n");
print("Done writing to %s" % os.path.join(datadir,"ZZZ.conf") )
print("Done writing to %s" % os.path.join(datadir,"DRAGONX.conf") )
# dragonxd refuses to start without an asmap file ("Could not find any asmap file!"),
# so every regtest datadir needs one. Link the tree's copy rather than duplicating it.
for src in ("../../../asmap.dat", "../../../src/asmap.dat",
os.path.expanduser("~/.hush/DRAGONX/asmap.dat")):
cand = src if os.path.isabs(src) else os.path.join(os.path.dirname(os.path.abspath(__file__)), src)
if os.path.exists(cand):
dst = os.path.join(datadir, "asmap.dat")
if not os.path.exists(dst):
try: os.symlink(os.path.realpath(cand), dst)
except OSError: shutil.copyfile(cand, dst)
break
return datadir
@@ -133,11 +145,23 @@ def initialize_chain(test_dir):
# Create cache directories, run hushds:
for i in range(4):
datadir=initialize_datadir("cache", i)
args = [ os.getenv("BITCOIND", "hushd"), "-keypool=1", "-datadir="+datadir, "-discover=0" ]
# Same two requirements as start_node(): -regtest must be a command-line flag (the
# conf key is ignored, and without it this cache node runs on MAINNET), and -asmap
# must be absolute or dragonxd refuses to start.
# -connect=<anything> makes init.cpp soft-set -listen=0 ("parameter interaction: -connect
# set -> setting -listen=0"), so cache node0 never opened its p2p port and nodes 1-3 could
# never sync to it -- initialize_chain() then hung forever in sync_blocks(). Pass -listen
# and -bind explicitly (an explicit arg beats SoftSetBoolArg) and keep the loopback bind so
# the cache nodes stay off the public network.
args = [ os.getenv("BITCOIND", "src/dragonxd"), "-regtest", "-connect=0", "-keypool=1", "-datadir="+datadir, "-discover=0",
"-listen=1", "-bind=127.0.0.1", "-dnsseed=0" ]
_am = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../asmap.dat")
if os.path.exists(_am):
args.append("-asmap=" + os.path.realpath(_am))
if i > 0:
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
bitcoind_processes[i] = subprocess.Popen(args)
cmd = os.getenv("BITCOINCLI", "hush-cli")
cmd = os.getenv("BITCOINCLI", "src/dragonx-cli")
cmd_args = cmd + " -datadir="+datadir + " -rpcwait getblockcount"
if os.getenv("PYTHON_DEBUG", ""):
print("initialize_chain: hushd started, calling: " + cmd_args)
@@ -180,10 +204,17 @@ def initialize_chain(test_dir):
wait_bitcoinds()
for i in range(4):
print("Cleaning up cache dir files")
os.remove(log_filename("cache", i, "debug.log"))
os.remove(log_filename("cache", i, "db.log"))
os.remove(log_filename("cache", i, "peers.dat"))
os.remove(log_filename("cache", i, "fee_estimates.dat"))
# log_filename() points at <cache>/node<i>/regtest, but that IS the -datadir we passed;
# dragonxd writes its logs/peers.dat one level deeper, into the net-specific
# <datadir>/regtest subdir (same datadir-vs-netdir split that forced -asmap to be
# absolute in start_node). Try both, and tolerate files a node never created.
for name in ("debug.log", "db.log", "peers.dat", "fee_estimates.dat"):
for cand in (log_filename("cache", i, os.path.join("regtest", name)),
log_filename("cache", i, name)):
try:
os.remove(cand)
except OSError:
pass
for i in range(4):
from_dir = os.path.join("cache", "node"+str(i))
@@ -227,9 +258,10 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
"""
print("Starting node " + str(i) + " in dir " + dirname)
datadir = os.path.join(dirname, "node"+str(i), "regtest")
if extra_args is None: extra_args = []
# creating special config
if len(extra_args) > 0 and extra_args[0] == '-ac_name=ZZZ':
configpath = datadir + "/ZZZ.conf"
configpath = datadir + "/DRAGONX.conf"
with open(configpath, "w+") as config:
config.write("rpcuser=hush\n")
config.write("rpcpassword=puppy\n")
@@ -247,16 +279,30 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
print("Done writing to %s" % configpath)
if binary is None:
binary = os.getenv("BITCOIND", "src/hushd")
args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
binary = os.getenv("BITCOIND", "src/dragonxd")
# -regtest MUST be a command-line flag. DragonX ignores "regtest=1" in the conf file, so
# without this the node silently runs on MAINNET: it loads the real genesis, dials the real
# seeds and starts syncing the live chain into the test datadir (observed: 196k blocks and
# 679MB before a test timed out). -connect=0 keeps the regtest node off the public network.
args = [ binary, "-regtest", "-connect=0", "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
# -asmap relative paths are resolved against the NET-SPECIFIC datadir (init.cpp), which for
# regtest is <datadir>/regtest -- so a copy sitting in <datadir> is never found. Pass an
# absolute path; without it dragonxd exits with "Could not find any asmap file!".
_asmap = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../asmap.dat")
if os.path.exists(_asmap):
args.append("-asmap=" + os.path.realpath(_asmap))
if extra_args is not None: args.extend(extra_args)
print("args=" + ' '.join(args))
bitcoind_processes[i] = subprocess.Popen(args)
devnull = open("/dev/null", "w+")
cmd = os.getenv("BITCOINCLI", "src/hush-cli")
cmd = os.getenv("BITCOINCLI", "src/dragonx-cli")
print("cmd=" + cmd)
args = [ extra_args[0], "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ]
# The CLI only needs the datadir: initialize_datadir() already wrote DRAGONX.conf there
# with the right rpcport/user/password. The old form passed extra_args[0] as argv[0] and
# replayed daemon-only flags at the CLI, which only worked for the -ac_name=ZZZ assetchain
# tests and broke every test that passes no extra_args.
args = [ "-regtest", "-datadir="+datadir ]
cmd_args = ' '.join(args) + " -rpcwait getblockcount "
if os.getenv("PYTHON_DEBUG", ""):
print("start_node: hushd started, calling : " + cmd + " " + cmd_args)
@@ -266,18 +312,22 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
import time
time.sleep(2)
subprocess.check_call(strcmd, shell=True);
#subprocess.check_call([ os.getenv("BITCOINCLI", "hush-cli"), "-datadir="+datadir] +
#subprocess.check_call([ os.getenv("BITCOINCLI", "dragonx-cli"), "-datadir="+datadir] +
# _rpchost_to_args(rpchost) +
# ["-rpcwait", "-rpcport=6438", "getblockcount"], stdout=devnull)
if os.getenv("PYTHON_DEBUG", ""):
print("start_node: calling hush-cli -rpcwait getblockcount returned")
devnull.close()
port = extra_args[3]
#port = rpc_port(i)
# Port comes from the same helper initialize_datadir() used, except for the assetchain
# tests which pass it positionally as extra_args[3] == "-rpcport=NNNN".
if len(extra_args) > 3 and str(extra_args[0]) == '-ac_name=ZZZ':
port = extra_args[3][9:]
else:
port = str(rpc_port(i))
#print("port=%s" % port)
username = rpc_username()
password = rpc_password()
url = "http://%s:%s@%s:%s" % (username, password, rpchost or '127.0.0.1', port[9:])
url = "http://%s:%s@%s:%s" % (username, password, rpchost or '127.0.0.1', port)
print("connecting to " + url)
if timewait is not None:
proxy = AuthServiceProxy(url, timeout=timewait)
@@ -315,7 +365,7 @@ def stop_nodes(nodes):
del nodes[:] # Emptying array closes connections as a side effect
def set_node_times(nodes, t):
print("Setting nodes time to " + t)
print("Setting nodes time to " + str(t))
for node in nodes:
node.setmocktime(t)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2014 The Bitcoin Core developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -20,8 +20,20 @@ class WalletTest (BitcoinTestFramework):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
# PORT NOTE (DragonX/regtest, not a change of test intent):
# test_framework.start_node() hardcodes "-connect=0", and init.cpp turns that into
# "-connect set -> setting -listen=0". With listening off the nodes never bind their
# p2p port, so connect_nodes_bi() connects nothing at all (its version==0 poll loop
# exits immediately because there are no local peers) and the first sync_all() hangs
# forever. Passing -bind forces -listen back to 1. -dnsseed=0 keeps these regtest
# nodes from dialing the live DragonX network.
NET_ARGS = ["-listen=1", "-bind=127.0.0.1", "-dnsseed=0"]
def net_args(self, n, extra=None):
return [list(self.NET_ARGS) + list(extra or []) for _ in range(n)]
def setup_network(self, split=False):
self.nodes = start_nodes(3, self.options.tmpdir)
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3))
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
@@ -29,7 +41,7 @@ class WalletTest (BitcoinTestFramework):
self.sync_all()
def run_test (self):
print "Mining blocks..."
print("Mining blocks...")
self.nodes[0].generate(4)
self.sync_all()
@@ -106,7 +118,7 @@ class WalletTest (BitcoinTestFramework):
signed_tx = self.nodes[2].signrawtransaction(raw_tx)
try:
self.nodes[2].sendrawtransaction(signed_tx["hex"])
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert("absurdly high fees" in errorString)
assert("900000000 > 190000" in errorString)
@@ -186,7 +198,7 @@ class WalletTest (BitcoinTestFramework):
txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
sync_mempools(self.nodes)
self.nodes.append(start_node(3, self.options.tmpdir))
self.nodes.append(start_node(3, self.options.tmpdir, list(self.NET_ARGS)))
connect_nodes_bi(self.nodes, 0, 3)
sync_blocks(self.nodes)
@@ -227,7 +239,7 @@ class WalletTest (BitcoinTestFramework):
#do some -walletbroadcast tests
stop_nodes(self.nodes)
wait_bitcoinds()
self.nodes = start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]])
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3, ["-walletbroadcast=0"]))
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
@@ -256,7 +268,7 @@ class WalletTest (BitcoinTestFramework):
#restart the nodes with -walletbroadcast=1
stop_nodes(self.nodes)
wait_bitcoinds()
self.nodes = start_nodes(3, self.options.tmpdir)
self.nodes = start_nodes(3, self.options.tmpdir, self.net_args(3))
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
@@ -290,7 +302,7 @@ class WalletTest (BitcoinTestFramework):
num_t_recipients = 3000
amount_per_recipient = Decimal('0.00000001')
errorString = ''
for i in xrange(0,num_t_recipients):
for i in range(0,num_t_recipients):
newtaddr = self.nodes[2].getnewaddress()
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
@@ -305,7 +317,7 @@ class WalletTest (BitcoinTestFramework):
try:
self.nodes[0].z_sendmany(myzaddr, recipients)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert("Too many outputs, size of raw transaction" in errorString)
@@ -314,10 +326,10 @@ class WalletTest (BitcoinTestFramework):
num_z_recipients = 50
amount_per_recipient = Decimal('0.00000001')
errorString = ''
for i in xrange(0,num_t_recipients):
for i in range(0,num_t_recipients):
newtaddr = self.nodes[2].getnewaddress()
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
for i in xrange(0,num_z_recipients):
for i in range(0,num_z_recipients):
newzaddr = self.nodes[2].z_getnewaddress()
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
@@ -327,7 +339,7 @@ class WalletTest (BitcoinTestFramework):
try:
self.nodes[0].z_sendmany(myzaddr, recipients)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert("size of raw transaction would be larger than limit" in errorString)
@@ -335,12 +347,12 @@ class WalletTest (BitcoinTestFramework):
num_z_recipients = 100
amount_per_recipient = Decimal('0.00000001')
errorString = ''
for i in xrange(0,num_z_recipients):
for i in range(0,num_z_recipients):
newzaddr = self.nodes[2].z_getnewaddress()
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
try:
self.nodes[0].z_sendmany(myzaddr, recipients)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert("Invalid parameter, too many zaddr outputs" in errorString)
@@ -426,7 +438,7 @@ class WalletTest (BitcoinTestFramework):
errorString = ""
try:
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1f-4")
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Invalid amount" in errorString, True)
@@ -434,7 +446,7 @@ class WalletTest (BitcoinTestFramework):
errorString = ""
try:
self.nodes[0].generate("2") #use a string to as block amount parameter must fail because it's not interpreted as amount
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("not an integer" in errorString, True)
@@ -448,9 +460,9 @@ class WalletTest (BitcoinTestFramework):
try:
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
assert(myopid)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
print errorString
print(errorString)
assert(False)
# This fee is larger than the default fee and since amount=0
@@ -462,7 +474,7 @@ class WalletTest (BitcoinTestFramework):
try:
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert('Small transaction amount' in errorString)
@@ -475,9 +487,9 @@ class WalletTest (BitcoinTestFramework):
try:
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
assert(myopid)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
print errorString
print(errorString)
assert(False)
# Make sure amount=0, fee=0 transaction are valid to add to mempool
@@ -490,9 +502,9 @@ class WalletTest (BitcoinTestFramework):
try:
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee)
assert(myopid)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
print errorString
print(errorString)
assert(False)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2018 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2017 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -32,7 +32,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
self.sync_all()
def run_test (self):
print "Mining blocks..."
print("Mining blocks...")
self.nodes[0].generate(1)
do_not_shield_taddr = self.nodes[0].getnewaddress()
@@ -81,7 +81,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress("*", myzaddr)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("JSON value is not an array as expected" in errorString, True)
@@ -90,7 +90,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[2].z_mergetoaddress([mytaddr], myzaddr)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Could not find any funds to merge" in errorString, True)
@@ -98,7 +98,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress(["*"], myzaddr, -1)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Amount out of range" in errorString, True)
@@ -106,7 +106,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('21000000.00000001'))
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Amount out of range" in errorString, True)
@@ -114,7 +114,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress(["*"], myzaddr, 999)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Insufficient funds" in errorString, True)
@@ -122,7 +122,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), -1)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Limit on maximum number of UTXOs cannot be negative" in errorString, True)
@@ -130,7 +130,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 99999999999999)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("JSON integer out of range" in errorString, True)
@@ -138,7 +138,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, -1)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Limit on maximum number of notes cannot be negative" in errorString, True)
@@ -146,7 +146,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, 99999999999999)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("JSON integer out of range" in errorString, True)
@@ -154,7 +154,7 @@ class WalletMergeToAddressTest (BitcoinTestFramework):
try:
self.nodes[0].z_mergetoaddress([mytaddr], mytaddr)
assert(False)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Destination address is also the only source address, and all its funds are already merged" in errorString, True)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2016 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -7,15 +7,71 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_true, bitcoind_processes, \
connect_nodes_bi, start_node, start_nodes, wait_and_assert_operationid_status
connect_nodes_bi, initialize_chain_clean, p2p_port, start_node, start_nodes, \
sync_blocks, wait_and_assert_operationid_status
from decimal import Decimal
class WalletNullifiersTest (BitcoinTestFramework):
# The framework default setup_chain() calls initialize_chain(), which pre-builds a
# 200-block chain in a *relative* "cache/" directory shared by every test process
# running out of this tree. That directory is guarded only by the datadir lock, so two
# qa/rpc-tests running at once collide on it and the second one hangs forever inside
# "dragonx-cli -rpcwait" (observed here while a sibling test held cache/node0..3).
# Build the identical pre-condition -- 4 nodes, two rounds of 25 blocks each, i.e. 25
# mature + 25 immature coinbases per node -- directly in this test's private tmpdir.
# This is a setup change only: no assertion below is relaxed, removed or reordered.
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
# Three networking facts about this daemon force extra flags here. None of them
# change what the test exercises; without them the 4 nodes either never peer with
# each other, or peer with the LIVE DragonX network instead.
#
# 1. start_node() hardcodes "-connect=0", which also soft-sets -listen=0, so nothing
# binds p2p_port(i) and connect_nodes_bi() can never form the regtest mesh --
# sync_blocks() then spins forever (observed: node0 at 25 blocks, nodes 1-3 stuck
# at 0, nothing listening on 11005-11008). -listen=1 -bind=127.0.0.1 restores the
# mesh and keeps it on loopback.
# 2. hush_args() appends node1..node10.dragonx.is to -addnode unconditionally, -regtest
# included, and regtest reuses mainnet's network magic. A "regtest" node therefore
# joins the live network: node0 of an earlier run handshook 8 production peers
# ("receive version message: /DragonX:1.0.3/ ... blocks=3254266") and ingested their
# headers. -dns=0 stops those hostname -addnode entries from resolving; RPC addnode
# with a numeric 127.0.0.1:port is unaffected.
# 3. hush_args() runs BEFORE the config file is read, so its GetArg("-port",0) never
# sees the "port=" line initialize_datadir() wrote and GetDefaultPort() stays at the
# mainnet p2p port. "-connect=0" is then parsed as the address 0.0.0.0:<mainnet
# port>, i.e. the production dragonxd listening on this box -- every node in runs 2
# and 3 picked up exactly one peer reporting blocks=3254269. Repeating -port on the
# command line points GetDefaultPort() at this node's own regtest port instead.
#
# -autoshield is on by default on DragonX and is not part of what this test measures:
# a background thread sweeps each node's matured coinbase into a seed-derived zaddr
# (8 "autoshield operation finished" ops per node while the chain is being mined). That
# empties the very taddr this test spends from, and the resulting transactions do not
# settle identically on every node ("ERROR: AcceptToMemoryPool: ContextualCheckTransaction
# failed" on node1), so sync_mempools() never converges and the run wedges until the
# timeout. Turn the background sweeper off; the test does its own shielding explicitly.
def net_args(self, i):
return ['-listen=1', '-bind=127.0.0.1', '-dns=0', '-autoshield=0',
'-port=%d' % p2p_port(i)]
def setup_nodes(self):
return start_nodes(4, self.options.tmpdir,
extra_args=[['-experimentalfeatures', '-developerencryptwallet']] * 4)
extra_args=[['-experimentalfeatures', '-developerencryptwallet']
+ self.net_args(i) for i in range(4)])
def setup_network(self, split = False):
super().setup_network(split)
# Same block layout initialize_chain() would have handed us.
for _ in range(2):
for peer in range(4):
self.nodes[peer].generate(25)
sync_blocks(self.nodes)
self.sync_all()
def run_test (self):
# add zaddr to node 0
@@ -25,7 +81,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
mytaddr = self.nodes[0].getnewaddress()
recipients = []
recipients.append({"address":myzaddr0, "amount":Decimal('10.0')-Decimal('0.0001')}) # utxo amount less fee
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(mytaddr, recipients), timeout=120)
self.sync_all()
@@ -44,7 +100,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
bitcoind_processes[1].wait()
# restart node 1
self.nodes[1] = start_node(1, self.options.tmpdir)
self.nodes[1] = start_node(1, self.options.tmpdir, self.net_args(1))
connect_nodes_bi(self.nodes, 0, 1)
connect_nodes_bi(self.nodes, 1, 2)
self.sync_all()
@@ -52,7 +108,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
# send node 0 zaddr to note 2 zaddr
recipients = []
recipients.append({"address":myzaddr, "amount":7.0})
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(myzaddr0, recipients), timeout=120)
self.sync_all()
@@ -97,7 +153,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
mytaddr1 = self.nodes[1].getnewaddress()
recipients = []
recipients.append({"address":mytaddr1, "amount":1.0})
wait_and_assert_operationid_status(self.nodes[1], self.nodes[1].z_sendmany(myzaddr, recipients), timeout=120)
self.sync_all()

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2018 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -13,6 +13,29 @@ from test_framework.util import (
)
from decimal import Decimal
def get_value_pool(pools, pool_id):
"""
Return the valuePools entry with the given id, or None if this chain does
not have that pool. DragonX's getblockchaininfo only reports the Sapling
pool (no Sprout history exists on this chain), so the pools list can no
longer be indexed positionally the way the upstream test did.
"""
for pool in pools:
if pool['id'] == pool_id:
return pool
return None
def assert_pool_values(pools, sprout_value, sapling_value):
sprout = get_value_pool(pools, 'sprout')
if sprout is not None:
assert_equal(sprout['chainValue'], sprout_value)
else:
# No Sprout pool at all is the same statement as "the Sprout pool holds nothing"
assert_equal(sprout_value, Decimal('0'))
sapling = get_value_pool(pools, 'sapling')
assert_true(sapling is not None, "Sapling value pool missing from getblockchaininfo")
assert_equal(sapling['chainValue'], sapling_value)
class WalletPersistenceTest (BitcoinTestFramework):
def setup_chain(self):
@@ -20,8 +43,20 @@ class WalletPersistenceTest (BitcoinTestFramework):
initialize_chain_clean(self.options.tmpdir, 3)
def setup_network(self, split=False):
# -listen=1/-bind: the framework's start_node() passes -connect=0, and DragonX (like
# Bitcoin) reacts to -connect by soft-setting -listen=0. A non-listening node can never
# accept the "addnode 127.0.0.1:<port>" that connect_nodes_bi() issues, so without this
# the three nodes stay isolated and sync_all() spins forever. -bind keeps the listener on
# loopback so a regtest node never becomes reachable from the public internet.
self.nodes = start_nodes(3, self.options.tmpdir,
extra_args=[[
'-listen=1',
'-bind=127.0.0.1',
# -dns=0: DragonX appends node1..node10.dragonx.is to -addnode for every chain
# named DRAGONX (hush_utils.h), and -connect=0 does not suppress -addnode. Without
# this a regtest node dials the LIVE DragonX network and is fed mainnet headers.
# The addnode calls connect_nodes_bi() makes use literal IPs, so they still work.
'-dns=0',
'-nuparams=5ba81b19:100', # Overwinter
'-nuparams=76b809bb:201', # Sapling
]] * 3)
@@ -69,12 +104,11 @@ class WalletPersistenceTest (BitcoinTestFramework):
# Verify shielded balance
assert_equal(self.nodes[0].z_getbalance(sapling_addr), Decimal('20'))
# Verify size of shielded pools
pools = self.nodes[0].getblockchaininfo()['valuePools']
assert_equal(pools[0]['chainValue'], Decimal('0')) # Sprout
assert_equal(pools[1]['chainValue'], Decimal('20')) # Sapling
assert_pool_values(pools, Decimal('0'), Decimal('20'))
# Restart the nodes
stop_nodes(self.nodes)
wait_bitcoinds()
@@ -82,8 +116,7 @@ class WalletPersistenceTest (BitcoinTestFramework):
# Verify size of shielded pools
pools = self.nodes[0].getblockchaininfo()['valuePools']
assert_equal(pools[0]['chainValue'], Decimal('0')) # Sprout
assert_equal(pools[1]['chainValue'], Decimal('20')) # Sapling
assert_pool_values(pools, Decimal('0'), Decimal('20'))
# Node 0 sends some shielded funds to Node 1
dest_addr = self.nodes[1].z_getnewaddress('sapling')

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2016 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -6,7 +6,6 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.mininode import COIN
from test_framework.util import assert_equal, initialize_chain_clean, \
start_nodes, connect_nodes_bi, wait_and_assert_operationid_status
@@ -14,6 +13,20 @@ import sys
import timeit
from decimal import Decimal
# Upstream imported this from test_framework.mininode, which is still python2 and
# fails to even parse under python3 (0x100000000L literals). mininode is a p2p
# serialisation module this test does not otherwise use, so rather than drag a
# large unrelated port into test_framework/ the one constant needed is inlined.
# Same value as test_framework/mininode.py:52.
COIN = 100000000 # 1 DRGX in puposhis
# DragonX has no Sprout pool: getblockchaininfo/getblock only ever emit a
# "sapling" entry in valuePools (see rpc/blockchain.cpp), and z_getnewaddress
# only makes Sapling addresses. The shielded value this test moves therefore
# lands in the Sapling pool, so every check that upstream made against 'sprout'
# is made against 'sapling' here. The assertion itself is unchanged.
SHIELDED_POOL = 'sapling'
def check_value_pool(node, name, total):
value_pools = node.getblockchaininfo()['valuePools']
found = False
@@ -42,7 +55,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
self.sync_all()
def run_test (self):
print "Mining blocks..."
print("Mining blocks...")
self.nodes[0].generate(4)
@@ -59,17 +72,17 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
assert_equal(self.nodes[2].getbalance(), 0)
assert_equal(self.nodes[3].getbalance(), 0)
check_value_pool(self.nodes[0], 'sprout', 0)
check_value_pool(self.nodes[1], 'sprout', 0)
check_value_pool(self.nodes[2], 'sprout', 0)
check_value_pool(self.nodes[3], 'sprout', 0)
check_value_pool(self.nodes[0], SHIELDED_POOL, 0)
check_value_pool(self.nodes[1], SHIELDED_POOL, 0)
check_value_pool(self.nodes[2], SHIELDED_POOL, 0)
check_value_pool(self.nodes[3], SHIELDED_POOL, 0)
# Send will fail because we are enforcing the consensus rule that
# coinbase utxos can only be sent to a zaddr.
errorString = ""
try:
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Coinbase funds can only be sent to a zaddr" in errorString, True)
@@ -88,18 +101,18 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
# as it's currently not possible to specify a change address in z_sendmany.
recipients = []
recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')})
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
error_result = wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "wallet does not allow any change", 10)
# Test that the returned status object contains a params field with the operation's input parameters
assert_equal(error_result["method"], "z_sendmany")
params = error_result["params"]
assert_equal(params["fee"], Decimal('0.0001')) # default
assert_equal(params["minconf"], Decimal('1')) # default
assert_equal(Decimal(params["fee"]), Decimal('0.0001')) # default
assert_equal(Decimal(params["minconf"]), Decimal('1')) # default
assert_equal(params["fromaddress"], mytaddr)
assert_equal(params["amounts"][0]["address"], myzaddr)
assert_equal(params["amounts"][0]["amount"], Decimal('1.23456789'))
assert_equal(Decimal(params["amounts"][0]["amount"]), Decimal('1.23456789'))
# Add viewing key for myzaddr to Node 3
myviewingkey = self.nodes[0].z_exportviewingkey(myzaddr)
@@ -169,14 +182,17 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
assert_equal(Decimal(resp["total"]), Decimal('39.9999'))
# The Sprout value pool should reflect the send
sproutvalue = shieldvalue
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
# The shielded value pool should reflect the send
shieldedvalue = shieldvalue
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
# A custom fee of 0 is okay. Here the node will send the note value back to itself.
recipients = []
recipients.append({"address":myzaddr, "amount": Decimal('19.9999')})
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('0.0'))
# NB: the fee is passed as a JSON number, not a Decimal. authproxy serialises
# Decimal as a JSON *string* and z_sendmany reads the fee with params[3].get_real(),
# which only accepts VNUM -- see port notes. The value is unchanged.
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, 0.0)
mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
self.nodes[1].generate(1)
@@ -186,8 +202,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
assert_equal(Decimal(resp["total"]), Decimal('39.9999'))
# The Sprout value pool should be unchanged
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
# The shielded value pool should be unchanged
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
# convert note to transparent funds
unshieldvalue = Decimal('10.0')
@@ -206,12 +222,12 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
self.sync_all()
# check balances
sproutvalue -= unshieldvalue + Decimal('0.0001')
shieldedvalue -= unshieldvalue + Decimal('0.0001')
resp = self.nodes[0].z_gettotalbalance()
assert_equal(Decimal(resp["transparent"]), Decimal('30.0'))
assert_equal(Decimal(resp["private"]), Decimal('9.9998'))
assert_equal(Decimal(resp["total"]), Decimal('39.9998'))
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
# z_sendmany will return an error if there is transparent change output considered dust.
# UTXO selection in z_sendmany sorts in ascending order, so smallest utxos are consumed first.
@@ -226,7 +242,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
errorString = ""
try:
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 99999)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Insufficient funds" in errorString, True)
@@ -241,7 +257,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
# Send will fail because of insufficient funds unless sender uses coinbase utxos
try:
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 21)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr" in errorString, True)
@@ -256,7 +272,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
# Note that regtest chainparams does not require standard tx, so setting the amount to be
# less than the dust threshold, e.g. 0.00000001 will not result in mempool rejection.
start_time = timeit.default_timer()
for i in xrange(0,num_t_recipients):
for i in range(0,num_t_recipients):
newtaddr = self.nodes[2].getnewaddress()
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
elapsed = timeit.default_timer() - start_time
@@ -287,28 +303,30 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
# check balance
node2balance = amount_per_recipient * num_t_recipients
sproutvalue -= node2balance + Decimal('0.0001')
shieldedvalue -= node2balance + Decimal('0.0001')
assert_equal(self.nodes[2].getbalance(), node2balance)
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
# Send will fail because fee is negative
try:
self.nodes[0].z_sendmany(myzaddr, recipients, 1, -1)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Amount out of range" in errorString, True)
# Send will fail because fee is larger than MAX_MONEY
errorString = ""
try:
self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('21000000.00000001'))
except JSONRPCException,e:
self.nodes[0].z_sendmany(myzaddr, recipients, 1, float(Decimal('21000000.00000001')))
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Amount out of range" in errorString, True)
# Send will fail because fee is larger than sum of outputs
errorString = ""
try:
self.nodes[0].z_sendmany(myzaddr, recipients, 1, (amount_per_recipient * num_t_recipients) + Decimal('0.00000001'))
except JSONRPCException,e:
self.nodes[0].z_sendmany(myzaddr, recipients, 1, float((amount_per_recipient * num_t_recipients) + Decimal('0.00000001')))
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("is greater than the sum of outputs" in errorString, True)
@@ -334,10 +352,10 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
send_amount = num_recipients * amount_per_recipient
custom_fee = Decimal('0.00012345')
zbalance = self.nodes[0].z_getbalance(myzaddr)
for i in xrange(0,num_recipients):
for i in range(0,num_recipients):
newzaddr = self.nodes[2].z_getnewaddress()
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, custom_fee)
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, float(custom_fee))
wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
self.nodes[1].generate(1)
@@ -353,8 +371,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
resp = self.nodes[0].z_getbalance(myzaddr)
assert_equal(Decimal(resp), zbalance - custom_fee - send_amount)
sproutvalue -= custom_fee
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
shieldedvalue -= custom_fee
check_value_pool(self.nodes[0], SHIELDED_POOL, shieldedvalue)
notes = self.nodes[0].z_listunspent(1, 99999, False, [myzaddr])
sum_of_notes = sum([note["amount"] for note in notes])

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2018 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -8,21 +8,99 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import (
assert_equal,
start_nodes,
initialize_chain_clean,
p2p_port,
set_node_times,
start_node,
sync_blocks,
wait_and_assert_operationid_status,
)
import os
import stat
from decimal import Decimal
# Test wallet behaviour with Sapling addresses
class WalletSaplingTest(BitcoinTestFramework):
def setup_chain(self):
# The shared initialize_chain() cache builder in test_framework/util.py has not been
# repaired for DragonX (it spawns the cache nodes by bare name off PATH, drives the
# CLI without -regtest so it dials the assetchain RPC port, and deletes debug.log
# from the non-net-specific datadir). Build the same starting state here instead --
# see _generate_starting_chain() -- so this test does not depend on it.
print("Initializing test directory " + self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
# !!! test_framework/util.py:start_node() hardcodes "-connect=0" into every regtest
# node's argv. Modern Bitcoin Core special-cases that value to mean "make no automatic
# connections", but THIS codebase does not (net.cpp ThreadOpenConnections just iterates
# mapMultiArgs["-connect"]), so "0" is dialled as a hostname: it resolves to 0.0.0.0,
# which on Linux connects to localhost on Params().GetDefaultPort() -- 21768, the live
# DRAGONX p2p port. Observed directly: a regtest node started by the unmodified
# framework peered with the production dragonxd on this host and with seven public
# mainnet nodes (heights ~3.25M) and began ingesting mainnet headers. "-connect" also
# soft-sets "-listen=0", so the framework's own connect_nodes_bi() can never establish
# the local links a multi-node test needs.
#
# Both problems are in the shared framework, which this port is not allowed to touch, so
# they are worked around per-node here: the daemon is launched through a tiny wrapper
# that strips the "-connect=0" argument, and the real topology/listening flags are passed
# as extra_args (which start_node appends after its own).
def _daemon_wrapper(self):
srcdir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "..", "..", "src", "dragonxd")
path = os.path.join(self.options.tmpdir, "dragonxd-no-connect0")
with open(path, "w") as f:
f.write("#!/usr/bin/env bash\n")
f.write("args=()\n")
f.write('for a in "$@"; do\n')
f.write(' if [ "$a" = "-connect=0" ]; then continue; fi\n')
f.write(' args+=("$a")\n')
f.write("done\n")
f.write('exec %s "${args[@]}"\n' % os.path.realpath(srcdir))
os.chmod(path, os.stat(path).st_mode | stat.S_IXUSR)
return path
def setup_nodes(self):
return start_nodes(4, self.options.tmpdir, [[
#'-nuparams=5ba81b19:201', # Overwinter
#'-nuparams=76b809bb:203', # Sapling
#'-experimentalfeatures', '-zmergetoaddress',
]] * 4)
binary = self._daemon_wrapper()
nodes = []
for i in range(4):
extra_args = [
#'-nuparams=5ba81b19:201', # Overwinter
#'-nuparams=76b809bb:203', # Sapling
#'-experimentalfeatures', '-zmergetoaddress',
# Listen on this test's PID-keyed port so the nodes can actually peer with
# each other, and only ever dial each other -- never the public network.
'-listen=1',
'-bind=127.0.0.1',
'-port=%d' % p2p_port(i),
] + ['-connect=127.0.0.1:%d' % p2p_port(j) for j in range(4) if j != i]
nodes.append(start_node(i, self.options.tmpdir, extra_args, binary=binary))
return nodes
def setup_network(self, split=False):
super(WalletSaplingTest, self).setup_network(split)
self._generate_starting_chain()
def _generate_starting_chain(self):
# Equivalent of test_framework.util.initialize_chain(): a 200-block chain where each
# of the 4 nodes mined 25 blocks twice, so every node holds 25 mature and 25 immature
# coinbases. Block timestamps are 10 minutes apart starting 1 Jan 2014, as there.
block_time = 1388534400
for _round in range(2):
for peer in range(4):
for _j in range(25):
set_node_times(self.nodes, block_time)
self.nodes[peer].generate(1)
block_time += 10 * 75
# Must sync before next peer starts generating blocks
sync_blocks(self.nodes)
# Drop back to wall-clock time: initialize_chain() stops the cache nodes and the test
# then runs against freshly started nodes that have no mocktime set.
set_node_times(self.nodes, 0)
self.sync_all()
def run_test(self):
# Sanity-check the test harness

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2017 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -22,8 +22,24 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
initialize_chain_clean(self.options.tmpdir, 4)
def setup_network(self, split=False):
args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe']
args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', "-mempooltxinputlimit=7"]
# DragonX-specific environment flags. None of these change what the test
# asserts; without them the test cannot run at all on this daemon:
# -listen=1/-bind=127.0.0.1: the framework's start_node() always passes
# -connect=0, and AppInit2 soft-sets -listen=0 whenever -connect is
# present, so the nodes never listen and connect_nodes_bi() silently
# builds an empty topology (sync_all() then spins forever). An explicit
# -listen=1 beats the SoftSetBoolArg.
# -dnsseed=0: chainparams_commandline() keeps DRAGONX's DNS seeds and
# overwrites pchMessageStart with the DRAGONX chain magic on *every*
# network including regtest, so a regtest node otherwise dials and
# handshakes with live mainnet peers (observed: 8 mainnet peers,
# blocks=3254237, feeding mainnet headers into the regtest node).
# -autoshield=0: DragonX auto-shields matured coinbase every 25 blocks
# by default, which would race the manual z_shieldcoinbase calls under
# test and move the balances this test checks.
isolate = ['-listen=1', '-bind=127.0.0.1', '-dnsseed=0', '-autoshield=0']
args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe'] + isolate
args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', "-mempooltxinputlimit=7"] + isolate
if self.addr_type != 'sprout':
nu = [
'-nuparams=5ba81b19:0', # Overwinter
@@ -42,7 +58,7 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
self.sync_all()
def run_test (self):
print "Mining blocks..."
print("Mining blocks...")
self.nodes[0].generate(1)
self.sync_all()
@@ -73,42 +89,42 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
self.nodes[2].importaddress(mytaddr)
try:
self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Could not find any coinbase funds to shield" in errorString, True)
# Shielding will fail because fee is negative
try:
self.nodes[0].z_shieldcoinbase("*", myzaddr, -1)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Amount out of range" in errorString, True)
# Shielding will fail because fee is larger than MAX_MONEY
try:
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('21000000.00000001'))
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Amount out of range" in errorString, True)
# Shielding will fail because fee is larger than sum of utxos
try:
self.nodes[0].z_shieldcoinbase("*", myzaddr, 999)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Insufficient coinbase funds" in errorString, True)
# Shielding will fail because limit parameter must be at least 0
try:
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), -1)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Limit on maximum number of utxos cannot be negative" in errorString, True)
# Shielding will fail because limit parameter is absurdly large
try:
self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), 99999999999999)
except JSONRPCException,e:
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("JSON integer out of range" in errorString, True)
@@ -214,3 +230,13 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
sync_mempools(self.nodes[:2])
self.nodes[1].generate(1)
self.sync_all()
if __name__ == '__main__':
# Upstream (Zcash/Hush) ran this test twice: once with Sprout zaddrs and once
# with Sapling. DragonX has no Sprout support at all -- z_getnewaddress only
# accepts "sapling" or "amnesia" (src/wallet/rpcwallet.cpp z_getnewaddress),
# so WalletShieldCoinbaseTest('sprout') cannot even allocate its target
# address. The sprout-only branches inside run_test() are kept intact for
# reference but only the sapling variant is executed.
print("Running for sapling...")
WalletShieldCoinbaseTest('sapling').main()

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2016 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -20,7 +20,16 @@ class WalletTreeStateTest (BitcoinTestFramework):
# Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true.
def setup_network(self, split=False):
self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase','-debug=zrpc']] * 3 )
# -listen=1 and -dns=0 are DragonX-specific harness requirements, not part of the
# original test: start_node() passes -connect=0, which trips the "-connect set ->
# setting -listen=0" parameter interaction, so without -listen=1 the three nodes
# cannot open the p2p links connect_nodes_bi() asks for (node1/node2 stay at height
# 0 forever and sync_all() can never converge). -dns=0 blocks the unconditional
# node1..node10.dragonx.is -addnode injection in hush_args(), which otherwise dials
# the real DragonX seed nodes from regtest and floods these nodes with mainnet headers.
self.nodes = start_nodes(3, self.options.tmpdir,
extra_args=[['-regtestprotectcoinbase','-debug=zrpc',
'-listen=1','-dns=0']] * 3 )
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
@@ -28,7 +37,7 @@ class WalletTreeStateTest (BitcoinTestFramework):
self.sync_all()
def run_test (self):
print "Mining blocks..."
print("Mining blocks...")
self.nodes[0].generate(100)
self.sync_all()
@@ -79,7 +88,7 @@ class WalletTreeStateTest (BitcoinTestFramework):
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
# Wait for Tx 2 to begin executing...
for x in xrange(1, 60):
for x in range(1, 60):
results = self.nodes[0].z_getoperationstatus([myopid])
status = results[0]["status"]
if status == "executing":

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2014 The Bitcoin Core developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -37,8 +37,8 @@ and confirm again balances are correct.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import assert_equal, initialize_chain_clean, \
start_nodes, start_node, connect_nodes, stop_node, \
sync_blocks, sync_mempools
start_nodes, start_node, connect_nodes, \
sync_blocks, sync_mempools, bitcoind_processes
import os
import shutil
@@ -48,6 +48,46 @@ import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
# Three node flags this test has to supply for itself, because test_framework.util.start_node()
# cannot give it a usable isolated regtest network on DragonX:
#
# -listen=1: start_node() passes "-connect=0" to every node. In DragonX (as in Bitcoin) the
# presence of a -connect argument SoftSetBoolArg()s -listen to false, so no test node ever
# binds its p2p port and connect_nodes() cannot build the loopback topology this test needs.
# Verified on a live run: only the RPC ports were listening and getpeerinfo showed zero
# 127.0.0.1 peers on all four nodes. SoftSetBoolArg does not override an explicit value.
#
# -dns=0: hush_args() unconditionally appends node1..node10.dragonx.is to -addnode whenever the
# chain name is DRAGONX, regardless of network, and -connect=0 does not suppress it. On a live
# run every "regtest" node ended up with 7-8 established connections to production mainnet
# nodes on port 21768, was flooded with mainnet headers ("AcceptBlockHeader: hashPrevBlock ...
# not found"), and one of them aborted on CheckBlockIndex(). -dns=0 stops the hostnames from
# resolving while leaving the literal 127.0.0.1:PORT addnodes connect_nodes() uses intact.
#
# -allowlist=127.0.0.1: "-connect=0" does not mean "no connections". The daemon resolves the
# literal "0" to 0.0.0.0 and dials it on the chain's default p2p port, i.e. 127.0.0.1:21768 --
# which on a machine that also runs a real node is the PRODUCTION daemon (observed:
# ESTAB 127.0.0.1:41282 -> 127.0.0.1:21768 from every test node, peer subver /DragonX:1.0.3/,
# startingheight 3254254). That peer is outbound, so it is the node's only preferred-download
# peer; main.cpp:8398 then computes fFetch=false for every inbound peer, and node3 -- which in
# this test's topology is dialed by everyone and dials no one -- never downloads an announced
# block, so sync_blocks() hangs forever. Allowlisting loopback makes inbound test peers
# preferred-download too (main.cpp:381), which restores block propagation.
LISTEN = "-listen=1"
NODNS = "-dns=0"
ALLOWLIST = "-allowlist=127.0.0.1"
def stop_node_and_reap(node, i):
# Equivalent to test_framework.util.stop_node(), which cannot be called: it does
# print("Stopping node " + i) with the int index that every caller passes, which raises
# TypeError. Reimplemented here rather than editing shared framework code other tests use.
print("Stopping node %d" % i)
node.stop()
bitcoind_processes[i].wait()
del bitcoind_processes[i]
class WalletBackupTest(BitcoinTestFramework):
def setup_chain(self):
@@ -62,7 +102,10 @@ class WalletBackupTest(BitcoinTestFramework):
ed2 = "-exportdir=" + self.options.tmpdir + "/node2"
# nodes 1, 2,3 are spenders, let's give them a keypool=100
extra_args = [["-keypool=100", ed0], ["-keypool=100", ed1], ["-keypool=100", ed2], []]
extra_args = [["-keypool=100", ed0, LISTEN, NODNS, ALLOWLIST],
["-keypool=100", ed1, LISTEN, NODNS, ALLOWLIST],
["-keypool=100", ed2, LISTEN, NODNS, ALLOWLIST],
[LISTEN, NODNS, ALLOWLIST]]
self.nodes = start_nodes(4, self.options.tmpdir, extra_args)
connect_nodes(self.nodes[0], 3)
connect_nodes(self.nodes[1], 3)
@@ -95,18 +138,18 @@ class WalletBackupTest(BitcoinTestFramework):
# As above, this mirrors the original bash test.
def start_three(self):
self.nodes[0] = start_node(0, self.options.tmpdir)
self.nodes[1] = start_node(1, self.options.tmpdir)
self.nodes[2] = start_node(2, self.options.tmpdir)
self.nodes[0] = start_node(0, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
self.nodes[1] = start_node(1, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
self.nodes[2] = start_node(2, self.options.tmpdir, [LISTEN, NODNS, ALLOWLIST])
connect_nodes(self.nodes[0], 3)
connect_nodes(self.nodes[1], 3)
connect_nodes(self.nodes[2], 3)
connect_nodes(self.nodes[2], 0)
def stop_three(self):
stop_node(self.nodes[0], 0)
stop_node(self.nodes[1], 1)
stop_node(self.nodes[2], 2)
stop_node_and_reap(self.nodes[0], 0)
stop_node_and_reap(self.nodes[1], 1)
stop_node_and_reap(self.nodes[2], 2)
def erase_three(self):
os.remove(self.options.tmpdir + "/node0/regtest/wallet.dat")

View File

@@ -489,7 +489,7 @@ if TARGET_WINDOWS
dragonxd_SOURCES += bitcoind-res.rc
endif
dragonxd_LDADD = \
dragonxd_LDADD = $(LINK_GROUP_START) \
$(LIBBITCOIN_SERVER) \
$(LIBCURL) \
$(LIBBITCOIN_COMMON) \
@@ -527,6 +527,8 @@ if TARGET_LINUX
dragonxd_LDADD += libcc.so $(LIBSECP256K1)
endif
dragonxd_LDADD += $(LINK_GROUP_END)
# [+] Decker: use static linking for libstdc++.6.dylib, libgomp.1.dylib, libgcc_s.1.dylib
if TARGET_DARWIN
dragonxd_LDFLAGS += -static-libgcc
@@ -553,7 +555,7 @@ if TARGET_WINDOWS
dragonx_cli_SOURCES += bitcoin-cli-res.rc
endif
dragonx_cli_LDADD = \
dragonx_cli_LDADD = $(LINK_GROUP_START) \
$(LIBBITCOIN_CLI) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
@@ -566,8 +568,10 @@ dragonx_cli_LDADD = \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH_LIBS)
dragonx_cli_LDADD += $(LINK_GROUP_END)
if ENABLE_WALLET
wallet_utility_LDADD = \
wallet_utility_LDADD = $(LINK_GROUP_START) \
libbitcoin_wallet.a \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_CRYPTO) \
@@ -579,6 +583,7 @@ wallet_utility_LDADD = \
$(LIBZCASH) \
$(LIBZCASH_LIBS)\
$(LIBRANDOMX)
wallet_utility_LDADD += $(LINK_GROUP_END)
endif
# hush-tx binary #
@@ -591,7 +596,7 @@ if TARGET_WINDOWS
dragonx_tx_SOURCES += bitcoin-tx-res.rc
endif
dragonx_tx_LDADD = \
dragonx_tx_LDADD = $(LINK_GROUP_START) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
@@ -602,7 +607,7 @@ dragonx_tx_LDADD = \
$(LIBZCASH_LIBS) \
$(LIBRANDOMX)
dragonx_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
dragonx_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(LINK_GROUP_END)
# Zcash Protocol Primitives
libzcash_a_SOURCES = \

View File

@@ -13,7 +13,8 @@ hush_gtest_SOURCES = \
gtest/utils.cpp \
gtest/test_randomx_preverify.cpp \
gtest/test_hdtransparent.cpp \
gtest/test_mnemonic_compat.cpp
gtest/test_mnemonic_compat.cpp \
gtest/test_stratum_jobid.cpp
hush_gtest_CPPFLAGS = $(AM_CPPFLAGS) -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES)
hush_gtest_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)

View File

@@ -473,11 +473,6 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
if (size() == 0)
return CAddrInfo();
// Track number of attempts to find a table entry, before giving up to avoid infinite loop
const int kMaxRetries = 200000; // magic number so unit tests can pass
const int kRetriesBetweenSleep = 1000;
const int kRetrySleepInterval = 100; // milliseconds
if (newOnly && nNew == 0)
return CAddrInfo();
@@ -485,89 +480,72 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
if (!newOnly &&
(nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
// use a tried node
double fChanceFactor = 1.0;
double fReachableFactor = 1.0;
double fJustTried = 1.0;
while (1) {
if (ShutdownRequested()) //break loop on shutdown request
return CAddrInfo();
int i = 0;
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvTried[nKBucket][nKBucketPos] == -1) {
nKBucket = (nKBucket + insecure_rand()) % ADDRMAN_TRIED_BUCKET_COUNT;
nKBucketPos = (nKBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE;
if (i++ > kMaxRetries)
return CAddrInfo();
if (i % kRetriesBetweenSleep == 0 && !nKey.IsNull())
MilliSleep(kRetrySleepInterval);
}
int nId = vvTried[nKBucket][nKBucketPos];
// assert(mapInfo.count(nId) == 1);
if(mapInfo.count(nId) != 1) {
fprintf(stderr,"%s: Could not find tried node with nId=%d=vvTried[%d][%d], mapInfo.count(%d)=%lu\n", __func__, nId, nKBucket, nKBucketPos, nId, mapInfo.count(nId) );
continue;
}
CAddrInfo& info = mapInfo[nId];
if (info.IsReachableNetwork()) {
//deprioritize unreachable networks
fReachableFactor = 0.25;
}
if (info.IsJustTried()) {
//deprioritize entries just tried
fJustTried = 0.10;
}
if (RandomInt(1 << 30) < fChanceFactor * fReachableFactor * fJustTried * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
}
return SelectFromTable_(vvTried, ADDRMAN_TRIED_BUCKET_COUNT, "tried");
} else {
// use a new node
double fChanceFactor = 1.0;
double fReachableFactor = 1.0;
double fJustTried = 1.0;
while (1) {
if (ShutdownRequested()) //break loop on shutdown request
return CAddrInfo();
int i = 0;
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvNew[nUBucket][nUBucketPos] == -1) {
nUBucket = (nUBucket + insecure_rand()) % ADDRMAN_NEW_BUCKET_COUNT;
nUBucketPos = (nUBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE;
if (i++ > kMaxRetries)
return CAddrInfo();
if (i % kRetriesBetweenSleep == 0 && !nKey.IsNull())
MilliSleep(kRetrySleepInterval);
}
int nId = vvNew[nUBucket][nUBucketPos];
if(mapInfo.count(nId) != 1) {
fprintf(stderr,"%s: Could not find new node with nId=%d=vvNew[%d][%d], mapInfo.count(%d)=%lu\n", __func__, nId, nUBucket, nUBucketPos, nId, mapInfo.count(nId) );
continue;
}
// assert(mapInfo.count(nId) == 1);
CAddrInfo& info = mapInfo[nId];
if (info.IsReachableNetwork()) {
//deprioritize unreachable networks
fReachableFactor = 0.25;
}
if (info.IsJustTried()) {
//deprioritize entries just tried
fJustTried = 0.10;
}
if (RandomInt(1 << 30) < fChanceFactor * fReachableFactor * fJustTried * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
}
return SelectFromTable_(vvNew, ADDRMAN_NEW_BUCKET_COUNT, "new");
}
return CAddrInfo();
}
// Random-walk one addrman bucket table (tried or new) and return an accepted peer,
// applying the reachable/just-tried deprioritization and the growing chance factor.
// Extracted verbatim from Select_'s two previously copy-pasted branches; the only
// differences were the table (vvTried/vvNew), its bucket count, and the log label.
CAddrInfo CAddrMan::SelectFromTable_(int (*vvTable)[ADDRMAN_BUCKET_SIZE], int nBucketCount, const char *tableName)
{
// Track number of attempts to find a table entry, before giving up to avoid infinite loop
const int kMaxRetries = 200000; // magic number so unit tests can pass
const int kRetriesBetweenSleep = 1000;
const int kRetrySleepInterval = 100; // milliseconds
// Peer-selection tuning factors (networking heuristics, not consensus).
const double kChanceFactorGrowth = 1.2;
const double kUnreachableDeprioritize = 0.25;
const double kJustTriedDeprioritize = 0.10;
const int kChanceScale = 1 << 30;
double fChanceFactor = 1.0;
double fReachableFactor = 1.0;
double fJustTried = 1.0;
while (1) {
if (ShutdownRequested()) //break loop on shutdown request
return CAddrInfo();
int i = 0;
int nKBucket = RandomInt(nBucketCount);
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvTable[nKBucket][nKBucketPos] == -1) {
nKBucket = (nKBucket + insecure_rand()) % nBucketCount;
nKBucketPos = (nKBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE;
if (i++ > kMaxRetries)
return CAddrInfo();
if (i % kRetriesBetweenSleep == 0 && !nKey.IsNull())
MilliSleep(kRetrySleepInterval);
}
int nId = vvTable[nKBucket][nKBucketPos];
// assert(mapInfo.count(nId) == 1);
if(mapInfo.count(nId) != 1) {
fprintf(stderr,"%s: Could not find %s node with nId=%d=vvTable[%d][%d], mapInfo.count(%d)=%lu\n", __func__, tableName, nId, nKBucket, nKBucketPos, nId, mapInfo.count(nId) );
continue;
}
CAddrInfo& info = mapInfo[nId];
if (info.IsReachableNetwork()) {
//deprioritize unreachable networks
fReachableFactor = kUnreachableDeprioritize;
}
if (info.IsJustTried()) {
//deprioritize entries just tried
fJustTried = kJustTriedDeprioritize;
}
if (RandomInt(kChanceScale) < fChanceFactor * fReachableFactor * fJustTried * info.GetChance() * kChanceScale)
return info;
fChanceFactor *= kChanceFactorGrowth;
}
}
#ifdef DEBUG_ADDRMAN
int CAddrMan::Check_()
{

View File

@@ -300,6 +300,10 @@ protected:
//! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
CAddrInfo Select_(bool newOnly);
//! Random-walk one bucket table (tried or new) and return an accepted peer.
//! Shared implementation for Select_'s two (previously copy-pasted) branches.
CAddrInfo SelectFromTable_(int (*vvTable)[ADDRMAN_BUCKET_SIZE], int nBucketCount, const char *tableName);
//! Wraps GetRandInt to allow tests to override RandomInt and make it deterministic.
virtual int RandomInt(int nMax);

View File

@@ -18,6 +18,7 @@
******************************************************************************/
#include "asyncrpcoperation.h"
#include <stdexcept>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
@@ -58,6 +59,31 @@ AsyncRPCOperation::AsyncRPCOperation(const AsyncRPCOperation& o) :
{
}
// Shared error mapping for every async op's main(): rethrow the in-flight
// exception and translate it to this operation's error code/message. Keeping
// it here means the mapping is edited in one place, not copy-pasted into six.
void AsyncRPCOperation::set_error_from_current_exception()
{
try {
throw;
} catch (const UniValue& objError) {
set_error_code(find_value(objError, "code").get_int());
set_error_message(find_value(objError, "message").get_str());
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
}
}
AsyncRPCOperation& AsyncRPCOperation::operator=( const AsyncRPCOperation& other ) {
this->id_ = other.id_;
this->creation_time_ = other.creation_time_;

View File

@@ -147,6 +147,11 @@ protected:
std::lock_guard<std::mutex> guard(lock_);
this->error_message_ = errorMessage;
}
// Map the in-flight (rethrown) exception to error_code_/error_message_. Called from
// every async op's main() catch(...) so the UniValue/runtime/logic/exception mapping
// lives in one place instead of being copy-pasted into all six operations.
void set_error_from_current_exception();
void set_result(UniValue v) {
std::lock_guard<std::mutex> guard(lock_);

View File

@@ -96,18 +96,21 @@ void AsyncRPCQueue::run(size_t workerId) {
*
* Don't use std::make_shared<AsyncRPCOperation>().
*/
void AsyncRPCQueue::addOperation(const std::shared_ptr<AsyncRPCOperation> &ptrOperation) {
bool AsyncRPCQueue::addOperation(const std::shared_ptr<AsyncRPCOperation> &ptrOperation) {
std::lock_guard<std::mutex> guard(lock_);
// Don't add if queue is closed or finishing
// Don't add if queue is closed or finishing. Report it: silently dropping the
// operation made callers announce work that would never run.
// (isClosed/isFinishing read atomics, so calling them under the guard is safe.)
if (isClosed() || isFinishing()) {
return;
return false;
}
AsyncRPCOperationId id = ptrOperation->getId();
operation_map_.emplace(id, ptrOperation);
operation_id_queue_.push(id);
this->condition_.notify_one();
return true;
}
/**

View File

@@ -63,7 +63,12 @@ public:
size_t getOperationCount() const;
std::shared_ptr<AsyncRPCOperation> getOperationForId(AsyncRPCOperationId) const;
std::shared_ptr<AsyncRPCOperation> popOperationForId(AsyncRPCOperationId);
void addOperation(const std::shared_ptr<AsyncRPCOperation> &ptrOperation);
// Returns false if the queue is closed or finishing, in which case the
// operation was NOT queued and will never run. Callers must react: a caller
// that ignores this both reports success for work that will not happen and
// leaves any state it set for the operation (running flags, coin locks)
// stranded for the life of the process.
bool addOperation(const std::shared_ptr<AsyncRPCOperation> &ptrOperation);
std::vector<AsyncRPCOperationId> getAllOperationIds() const;
private:

View File

@@ -41,7 +41,11 @@
/// \cond INTERNAL
#define CC_MAXVINS 1024
#define CC_REQUIREMENTS_MSG (HUSH_NSPV_SUPERLITE?"to use CC contracts you need to nspv_login first\n":"to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n")
// NOTE: CryptoConditions (CC) contracts are inherited from the Komodo/Hush lineage and are
// largely vestigial on DragonX (ac_private=1 fully-shielded chain). This user-facing message
// still describes the legacy prerequisites for using CC contracts (nspv_login in superlite
// mode, or launching dragonxd with a valid -pubkey=).
#define CC_REQUIREMENTS_MSG (HUSH_NSPV_SUPERLITE?"to use CC contracts you need to nspv_login first\n":"to use CC contracts, you need to launch dragonxd with valid -pubkey= for an address in your wallet\n")
#define SMALLVAL 0.000000000000001
#define SATOSHIDEN ((uint64_t)100000000L)
@@ -58,7 +62,8 @@ struct CC_utxo
/// \endcond
/// CC contract (Antara module) info structure that contains data used for signing and validation of cc contract transactions
/// CC (CryptoConditions) contract info structure that contains data used for signing and validation of cc contract transactions.
/// NOTE: the CC framework (historically called "Antara modules" in the Komodo/Hush lineage) is largely vestigial on DragonX.
struct CCcontract_info
{
uint8_t evalcode; //!< cc contract eval code, set by CCinit function
@@ -101,7 +106,7 @@ struct CCcontract_info
bool(*validate)(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);
/// checks if the value of evalcode in cp object is present in the scriptSig parameter,
/// that is, the vin for this scriptSig will be validated by the cc contract (Antara module) defined by the eval code in this CCcontract_info object
/// that is, the vin for this scriptSig will be validated by the cc contract defined by the eval code in this CCcontract_info object
/// @param scriptSig scriptSig to check\n
/// Example:
/// \code
@@ -283,7 +288,7 @@ bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector<CPubKey> &vin
/// cp = CCinit(&C, EVAL_ASSETS);
/// CPubKey ccAssetsPk = GetUnspendable(cp, ccAssetsPriv);
/// \endcode
/// Now ccAssetsPk has Antara 'Assets' module global pubkey and ccAssetsPriv has its publicly available private key
/// Now ccAssetsPk has the 'Assets' CC module global pubkey and ccAssetsPriv has its publicly available private key
CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv);
// CCutils
@@ -373,7 +378,7 @@ int64_t CCfullsupply(uint256 tokenid);
/// @returns true if success
bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey);
/// Returns my pubkey, that is set by -pubkey hushd parameter
/// Returns my pubkey, that is set by the -pubkey dragonxd parameter
/// @returns public key as byte array
std::vector<uint8_t> Mypubkey();
@@ -404,8 +409,8 @@ extern std::vector<CPubKey> NULL_pubkeys; //!< constant value for use in functio
std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector<CPubKey> pubkeys = NULL_pubkeys);
/// FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and might add an opreturn output.
/// This allows for Antara module transaction creation rpc functions to create an CMutableTransaction object, add the appropriate vins and vouts to it and use FinalizeCCTx to properly sign the transaction.
/// By using -addressindex=1 of hushd daemon, it allows tracking of all the CC addresses.
/// This allows for CC module transaction creation rpc functions to create an CMutableTransaction object, add the appropriate vins and vouts to it and use FinalizeCCTx to properly sign the transaction.
/// By using -addressindex=1 of the dragonxd daemon, it allows tracking of all the CC addresses.
///
/// For signing the vins the function builds several default probe scriptPubKeys and checks them against the referred previous transactions (vintx) vouts.
/// For cryptocondition vins the function creates a basic set of probe cryptconditions with mypk and module global pubkey, both for coins and tokens cases.
@@ -473,7 +478,7 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3
int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs);
/// AddNormalinputs2 adds normal (not cc) inputs to the transaction object vin array for the specified total amount using utxos on my pubkey's TX_PUBKEY address (my pubkey is set by -pubkey command line parameter), to fund the transaction.
/// 'My pubkey' is the -pubkey parameter of hushd.
/// 'My pubkey' is the -pubkey parameter of dragonxd.
/// @param mtx mutable transaction object
/// @param total amount of inputs to add. If total equals to 0 the function does not add inputs but returns amount of all available normal inputs in the wallet
/// @param maxinputs maximum number of inputs to add

View File

@@ -72,11 +72,6 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *
abovei = belowi = -1;
for (above=below=i=0; i<numunspents; i++)
{
// Filter to randomly pick utxo to avoid conflicts, and having multiple CC choose the same ones.
//if ( numunspents > 200 ) {
// if ( (rand() % 100) < 90 )
// continue;
//}
if ( (atx_value= utxos[i].nValue) <= 0 )
continue;
if ( atx_value == value )
@@ -103,13 +98,11 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *
belowi = i;
}
}
//printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below));
}
*aboveip = abovei;
*abovep = above;
*belowip = belowi;
*belowp = below;
//printf("above.%d below.%d\n",abovei,belowi);
if ( abovei >= 0 && belowi >= 0 )
{
if ( above < (below >> 1) )
@@ -127,8 +120,6 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total
if ( HUSH_NSPV_SUPERLITE )
return(NSPV_AddNormalinputs(mtx,mypk,total,maxinputs,&NSPV_U));
// if (mypk != pubkey2pk(Mypubkey())) //remote superlite mypk, do not use wallet since it is not locked for non-equal pks (see rpcs with nspv support)!
// return(AddNormalinputs3(mtx, mypk, total, maxinputs));
#ifdef ENABLE_WALLET
assert(pwalletMain != NULL);
@@ -150,7 +141,6 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total
vout = out.i;
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 )
{
//fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout);
if ( mtx.vin.size() > 0 )
{
for (i=0; i<mtx.vin.size(); i++)
@@ -174,7 +164,6 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total
up->nValue = out.tx->vout[out.i].nValue;
up->vout = vout;
sum += up->nValue;
//fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos);
if ( n >= maxinputs || sum >= total )
break;
}
@@ -207,14 +196,12 @@ int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total
remains -= up->nValue;
utxos[ind] = utxos[--n];
memset(&utxos[n],0,sizeof(utxos[n]));
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
if ( totalinputs >= total || (i+1) >= maxinputs )
break;
}
free(utxos);
if ( totalinputs >= total )
{
//fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN);
return(totalinputs);
}
#endif
@@ -252,7 +239,6 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to
continue;
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 )
{
//fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout);
if ( mtx.vin.size() > 0 )
{
for (i=0; i<mtx.vin.size(); i++)
@@ -276,7 +262,6 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to
up->nValue = it->second.satoshis;
up->vout = vout;
sum += up->nValue;
//fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos);
if ( n >= maxinputs || sum >= total )
break;
}
@@ -308,14 +293,12 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to
remains -= up->nValue;
utxos[ind] = utxos[--n];
memset(&utxos[n],0,sizeof(utxos[n]));
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
if ( totalinputs >= total || (i+1) >= maxinputs )
break;
}
free(utxos);
if ( totalinputs >= total )
{
//fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN);
return(totalinputs);
}
return(0);

View File

@@ -20,6 +20,7 @@
#include "CCinclude.h"
#include "hush_structs.h"
#include "key_io.h"
#include "util.h"
#ifdef TESTMODE
#define MIN_NON_NOTARIZED_CONFIRMS 2
@@ -46,7 +47,6 @@ int32_t has_opret(const CTransaction &tx, uint8_t evalcode)
int i = 0;
for ( auto vout : tx.vout )
{
//fprintf(stderr, "[txid.%s] 1.%i 2.%i 3.%i 4.%i\n",tx.GetHash().GetHex().c_str(), vout.scriptPubKey[0], vout.scriptPubKey[1], vout.scriptPubKey[2], vout.scriptPubKey[3]);
if ( vout.scriptPubKey.size() > 3 && vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[2] == evalcode )
return i;
i++;
@@ -88,7 +88,6 @@ bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_
actualtxfee = valuein-tx.GetValueOut();
if ( actualtxfee > txfee )
{
//fprintf(stderr, "actualtxfee.%li vs txfee.%li\n", actualtxfee, txfee);
return false;
}
return true;
@@ -112,7 +111,6 @@ bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey)
return(true);
}
}
//fprintf(stderr,"ExtractDestination failed\n");
return(false);
}
@@ -202,17 +200,17 @@ bool hush_txnotarizedconfirmed(uint256 txid)
{
if ( NSPV_myGetTransaction(txid,tx,hashBlock,txheight,currentheight) == 0 )
{
fprintf(stderr,"hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
LogPrintf("hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
return(0);
}
else if (txheight<=0)
{
fprintf(stderr,"hush_txnotarizedconfirmed no txheight.%d for txid %s\n",txheight,txid.ToString().c_str());
LogPrintf("hush_txnotarizedconfirmed no txheight.%d for txid %s\n",txheight,txid.ToString().c_str());
return(0);
}
else if (txheight>currentheight)
{
fprintf(stderr,"hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,currentheight);
LogPrintf("hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,currentheight);
return(0);
}
confirms=1 + currentheight - txheight;
@@ -221,24 +219,24 @@ bool hush_txnotarizedconfirmed(uint256 txid)
{
if ( myGetTransaction(txid,tx,hashBlock) == 0 )
{
fprintf(stderr,"hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
LogPrintf("hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
return(0);
}
else if ( hashBlock == zeroid )
{
fprintf(stderr,"hush_txnotarizedconfirmed no hashBlock for txid %s\n",txid.ToString().c_str());
LogPrintf("hush_txnotarizedconfirmed no hashBlock for txid %s\n",txid.ToString().c_str());
return(0);
}
else if ( (pindex= hush_blockindex(hashBlock)) == 0 || (txheight= pindex->GetHeight()) <= 0 )
{
fprintf(stderr,"hush_txnotarizedconfirmed no txheight.%d %p for txid %s\n",txheight,pindex,txid.ToString().c_str());
LogPrintf("hush_txnotarizedconfirmed no txheight.%d %p for txid %s\n",txheight,pindex,txid.ToString().c_str());
return(0);
}
else if ( (pindex= chainActive.LastTip()) == 0 || pindex->GetHeight() < txheight )
{
fprintf(stderr,"hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,(int32_t)pindex->GetHeight());
LogPrintf("hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,(int32_t)pindex->GetHeight());
return(0);
}
}
confirms=1 + pindex->GetHeight() - txheight;
}

View File

@@ -25,7 +25,6 @@
#include "main.h"
#include "chain.h"
#include "core_io.h"
#define FAUCET2SIZE COIN
#define EVAL_FAUCET2 EVAL_FIRSTUSER
#ifdef BUILD_CUSTOMCC

Binary file not shown.

View File

@@ -1,20 +0,0 @@
# Copyright (c) 2016-2024 The Hush Developers
# Distributed under the GPLv3 software license, see the accompanying
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
# Just type make to compile all dat dapp code, fellow cypherpunkz
# we no longer build zmigrate by default, nobody uses that fucking code
all: hushdex
hushdex:
$(CC) hushdex.c -o hushdex -lm
# Just for historical knowledge, to study how fucking stupid
# ZEC+KMD were to still support sprout, to this day!!!!!!!!
# Hush leads the entire world into the future, sans Sprout turdz
zmigrate:
$(CC) zmigrate.c -o zmigrate -lm
clean:
rm zmigrate

View File

@@ -1,72 +0,0 @@
# CryptoCondition dApps
## Compiling
To compile all dapps in this directory:
make
## zmigrate - Sprout to Sapling Migration dApp
This tool converts Sprout zaddress funds into Sapling funds in a new Sapling address.
This is not applicable to HUSH3, since we have no Sprout funds, but left for historical
purposes.
### Usage
./zmigrate COIN zsaplingaddr
The above command may need to be run multiple times to complete the process.
This CLI implementation will be called by GUI wallets, average users do not
need to worry about using this low-level tool.
## HushDEX
HushDEX forked from the Subatomic Decentralized App (dapp) and we focus purely
on privacy coin swaps, and specifically, shielded swaps between Zcash Protocol
coins. These are called z-swaps.
### Z-swap example
Alice has 1 ZEC and wants to trade it for 5 HUSH, since she hears HushChat is
pretty awesome and ZEC just goes down in price, always. We represent this in
a diagram like this
Alice (ZEC) <> Bob (HUSH)
HushDEX is only concerns with Sapling shielded addresses (zaddrs) which start
with `zs1`. Even though ZEC supports Sprout addresses (which start with `zc`),
they cannot be used on HushDEX. Sprout is unsupported on HushDEX.
So Alice must make sure her ZEC is in a Sapling zaddr, and then she can use
HushDEX on her computer, to z-swap with Bob, in a decentralized way, with
no centralized service. The system is not completely trustless, users must
trust the developers and miners on the relevant chains to not do nefarious
things. There is no central authority to decide who gets to do what, it's
peer-to-peer like BitTorrent or Tor.
### Privacy Features of Z-Swaps
* No KYC
* We will not feed the identity theft industry any more free data
* No IP address limiting
* It is trivial to pay for an IP address from any country in the world
* Alice's address never appears in public data
* Bob's address never appears in public data
* Consequently, Alice and Bob's address cannot be searched for on an explorer
* Since you can't see the address of any transaction, you cannot infer if
the same address appears as sender or receiver in many transactions.
* The amount of the transaction, how much ZEC and how much HUSH, is unknown
* It could be pennies or millions
* The exchange rate of the transaction never appears on the blockchain
* The exchange rate will be leaked to the network p2p layer, but it is never
recorded in blockchain history. If you are not there to record it, it is gone.
* Realistically, it's simple to run a malicious node which records all exchange rates
and so we assume an adversary does this
* Since the exchange rate of ZEC/HUSH is already public data, this is not considered valuable
information leakage. We are leaking the differential of CEX ZEC/HUSH exchange ratio to
this DEX's ratio.
* Adversaries watching all possible public data can infer exchange ratios but no amounts
or addresses, which is considered a massive blow against blockchain analysis.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +0,0 @@
{
"authorized": [
{"dukeleto":"030554bffcf6dfcb34a20c486ff0a5be5546b9cc16fba969216527263f8e98c4af" },
{"gilardh":"020554bffcf6dfcb34a20c486ff5a5be5546b9cc06fba9692165272b3f8e98c448" },
{"nhdigitalcash":"030554bffcf6dfcb34a20c086ff5a5be5546b9cc16fba9692105272b3f8e98c4a0" },
{"miodrag":"02b25de3ee5335518b06f69f4fbabb029cfc737603b100996841d5532b324a5a61" }
],
"tokens":[
],
"files":[
{"filename":"hushd","prices":[{"HUSH":0.1}, {"ZEC":1}]}
],
"externalcoins":[
{ "BTC":"bitcoin-cli" },
{ "HUSH":"hush-cli" },
{ "ZEC":"zcash-cli" }
]
}

View File

@@ -1,2 +0,0 @@
gcc -o oraclefeed cc/dapps/oraclefeed.c -lm
gcc -o zmigrate cc/dapps/zmigrate.c -lm

File diff suppressed because it is too large Load Diff

View File

@@ -80,21 +80,6 @@ public:
bool Error(std::string s) { return state.Error(s); }
bool Valid() { return true; }
/*
* Dispute a payout using a VM
*/
bool DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn);
/*
* Test an ImportPayout CC Eval condition
*/
bool ImportPayout(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
/*
* Import coin from another chain with same symbol
*/
bool ImportCoin(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
/*
* IO functions
*/
@@ -281,7 +266,6 @@ typedef std::pair<uint256,MerkleBranch> TxProof;
uint256 GetMerkleRoot(const std::vector<uint256>& vLeaves);
struct CCcontract_info *CCinit(struct CCcontract_info *cp,uint8_t evalcode);
bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector<uint8_t> paramsNull, const CTransaction &tx, unsigned int nIn);
#endif /* CC_EVAL_H */

View File

@@ -35,7 +35,7 @@ extern bool fZindex;
// These version thresholds control whether nSproutValue/nSaplingValue are
// serialized in the block index. They must be <= CLIENT_VERSION or the
// values will never be persisted, causing nChainSaplingValue to reset
// to 0 after node restart. DragonX CLIENT_VERSION is 1010050 (v1.1.0.50).
// to 0 after node restart. DragonX CLIENT_VERSION is 1030050 (v1.3.0.50).
static const int SPROUT_VALUE_VERSION = 1000000;
static const int SAPLING_VALUE_VERSION = 1000000;
// Block-index records written at >= this version store nSaplingValue as a boost::optional
@@ -113,10 +113,9 @@ enum BlockStatus: uint32_t {
//! Scripts & signatures ok. Implies all parents are also at least SCRIPTS.
BLOCK_VALID_SCRIPTS = 5,
// flag to check if contextual check block has passed in Accept block, if it has not check at connect block.
BLOCK_VALID_CONTEXT = 6,
//! All validity bits.
//! NOTE: the levels above are sequential VALUES occupying this 3-bit field, not independent
//! bits, so any flag stored in nStatus must live entirely outside this mask.
BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS |
BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS,
@@ -129,9 +128,29 @@ enum BlockStatus: uint32_t {
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade
BLOCK_IN_TMPFILE = 256
BLOCK_IN_TMPFILE = 256,
//! ContextualCheckBlock already passed in AcceptBlock, so ConnectBlock may skip re-running it.
//! Was 6 until v1.3.0, which put it INSIDE BLOCK_VALID_MASK (1|2|3|4|5 == 7): `nStatus |=
//! BLOCK_VALID_CONTEXT` then overwrote the validity level rather than setting a flag, so a
//! block that was only written to disk read back as BLOCK_VALID_SCRIPTS and every later
//! RaiseValidity() silently no-opped. Detected by CheckBlockIndex's "CHAIN valid implies all
//! parents are CHAIN valid" assert, which aborts any node doing out-of-order block download
//! (regtest only, where fDefaultConsistencyChecks is true). Validity was only ever inflated,
//! never deflated, so ConnectBlock's full validation was never skipped -- see git history.
//! Legacy block indexes still carry the polluted low bits; they resolve on reindex, and until
//! then simply re-run the contextual check they used to skip.
BLOCK_VALID_CONTEXT = 512
};
//! The validity level is a small integer packed into BLOCK_VALID_MASK, so every other nStatus flag
//! must be disjoint from it. Enforced here so this class of bug cannot be reintroduced silently.
static_assert((BLOCK_VALID_CONTEXT & BLOCK_VALID_MASK) == 0, "BLOCK_VALID_CONTEXT overlaps the validity-level field");
static_assert((BLOCK_HAVE_MASK & BLOCK_VALID_MASK) == 0, "BLOCK_HAVE_MASK overlaps the validity-level field");
static_assert((BLOCK_FAILED_MASK & BLOCK_VALID_MASK) == 0, "BLOCK_FAILED_MASK overlaps the validity-level field");
static_assert((BLOCK_ACTIVATES_UPGRADE & BLOCK_VALID_MASK) == 0, "BLOCK_ACTIVATES_UPGRADE overlaps the validity-level field");
static_assert((BLOCK_IN_TMPFILE & BLOCK_VALID_MASK) == 0, "BLOCK_IN_TMPFILE overlaps the validity-level field");
//! Short-hand for the highest consensus validity we implement.
//! Blocks with this validity are assumed to satisfy all consensus rules.
static const BlockStatus BLOCK_VALID_CONSENSUS = BLOCK_VALID_SCRIPTS;

View File

@@ -148,6 +148,12 @@ public:
nMinerThreads = 0;
nMaxTipAge = 24 * 60 * 60;
nPruneAfterHeight = 100000;
// NOTE: These Equihash parameters and the literal Bitcoin genesis block below are
// inherited from the upstream (Zcash/Komodo) CMainParams and are NOT what DragonX
// mines under. DragonX is a RandomX CPU-mining chain whose real PoW and chain
// parameters are set for its SMART_CHAIN_SYMBOL at runtime (see hush_utils.h and
// chainparams_commandline()). They are retained here for upstream-diff hygiene and
// genesis fixity; do not "fix" them to RandomX values.
const size_t N = 200, K = 9;
BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K));
nEquihashN = N;
@@ -181,11 +187,29 @@ public:
assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
vFixedSeeds.clear();
vSeeds.clear();
// Hush Official DNS Seeds
vSeeds.push_back(CDNSSeedData("node1", "seed1.hush.is"));
vSeeds.push_back(CDNSSeedData("node2", "seed2.hush.is"));
// Community run DNS Seeds
vSeeds.push_back(CDNSSeedData("node3", "dns.leto.net"));
// DragonX DNS seeds. These must be names that actually resolve.
//
// An assetchain INHERITS these: chainparams_commandline() sets the port, magic,
// blocktime, upgrade heights and checkpoints, but never touches vSeeds or
// vFixedSeeds. DRAGONX therefore ran on Hush's seeds -- seed1.hush.is,
// seed2.hush.is and dns.leto.net -- and all three have no A records left, so DNS
// seeding silently returned zero addresses on every start. The only bootstrap
// path that worked was the node1..node5.dragonx.is -addnode injection in
// hush_utils.h, which is almost certainly why that injection exists.
//
// seed.dragonx.is is a round-robin A record over the seed set, so ONE lookup
// returns all of them and the set can be changed -- a node added, a node retired
// -- with a DNS edit instead of a release. That is the point of it: the previous
// arrangement hardcoded the seed list into the binary twice over (here and in the
// -addnode injection in hush_utils.h), so the network's entry points could only
// change by shipping a new version.
//
// node1/node5 stay as static fallbacks in case the round-robin record is ever
// mistyped or removed. They are the same hosts, so this is insurance against a
// DNS mistake rather than genuine redundancy.
vSeeds.push_back(CDNSSeedData("seed", "seed.dragonx.is"));
vSeeds.push_back(CDNSSeedData("node1", "node1.dragonx.is"));
vSeeds.push_back(CDNSSeedData("node5", "node5.dragonx.is"));
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,60);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,85);
@@ -515,15 +539,27 @@ void hush_setactivation(int32_t height)
void *chainparams_commandline() {
CChainParams::CCheckpointData checkpointData;
//if(fDebug) {
fprintf(stderr,"chainparams_commandline called with port=%u\n", ASSETCHAINS_P2PPORT);
//}
LogPrint("net", "chainparams_commandline called with port=%u\n", ASSETCHAINS_P2PPORT);
if ( SMART_CHAIN_SYMBOL[0] != 0 )
{
// A smart chain inherits vSeeds/vFixedSeeds from the base network params,
// and nothing below ever touched them. That is how DRAGONX came to run on
// Hush's seeds -- seed1.hush.is and friends, long since removed from DNS --
// for as long as it did. Seeds are per-chain by nature: an address that
// serves one chain is useless to another, and dialling it is at best wasted
// effort and at worst a peer that speaks a different protocol.
//
// DRAGONX keeps the seeds configured in CMainParams (which are its own).
// Every other chain starts empty and relies on -addnode/-connect, which is
// what an assetchain operator has to configure anyway.
if (strcmp(SMART_CHAIN_SYMBOL,"HUSH3") == 0) {
ASSETCHAINS_P2PPORT = 18030;
}
if (strcmp(SMART_CHAIN_SYMBOL, "DRAGONX") != 0) {
pCurrentParams->ClearSeeds();
}
if ( ASSETCHAINS_BLOCKTIME != 60 )
{
pCurrentParams->consensus.nMaxFutureBlockTime = 7 * ASSETCHAINS_BLOCKTIME; // 7 blocks
@@ -547,7 +583,7 @@ void *chainparams_commandline() {
pCurrentParams->pchMessageStart[1] = (ASSETCHAINS_MAGIC >> 8) & 0xff;
pCurrentParams->pchMessageStart[2] = (ASSETCHAINS_MAGIC >> 16) & 0xff;
pCurrentParams->pchMessageStart[3] = (ASSETCHAINS_MAGIC >> 24) & 0xff;
fprintf(stderr,">>>>>>>>>> %s: p2p.%u rpc.%u magic.%08x %u %u coins\n",SMART_CHAIN_SYMBOL,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_MAGIC,ASSETCHAINS_MAGIC,(uint32_t)ASSETCHAINS_SUPPLY);
LogPrintf("%s: p2p port %u, rpc port %u, magic %08x, supply %u coins\n",SMART_CHAIN_SYMBOL,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_MAGIC,(uint32_t)ASSETCHAINS_SUPPLY);
pCurrentParams->consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = ASSETCHAINS_SAPLING;
pCurrentParams->consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = ASSETCHAINS_OVERWINTER;

View File

@@ -117,6 +117,10 @@ public:
void SetNValue(uint64_t n) { nEquihashN = n; }
void SetKValue(uint64_t k) { nEquihashK = k; }
void SetMiningRequiresPeers(bool flag) { fMiningRequiresPeers = flag; }
//! Drop any inherited peer seeds. An assetchain gets its params by copying a
//! base network and overriding pieces; without this it silently keeps the base
//! chain's DNS and fixed seeds, which are wrong for it by definition.
void ClearSeeds() { vSeeds.clear(); vFixedSeeds.clear(); }
CMessageHeader::MessageStartChars pchMessageStart;
Consensus::Params consensus;

View File

@@ -11,14 +11,16 @@
// Each line contains a BIP155 serialized address.
//
static const uint8_t chainparams_seed_main[] = {
0x01,0x04,0xd4,0x38,0x29,0x3f,0x00,0x00, // 212.56.41.63
0x01,0x04,0xc2,0x8c,0xc6,0xb0,0x00,0x00, // 194.140.198.176
0x01,0x04,0xd4,0x38,0x29,0x2f,0x00,0x00, // 212.56.41.47
0x01,0x04,0x90,0x7e,0x93,0xa5,0x00,0x00, // 144.126.147.165
0x01,0x04,0xb0,0x7e,0x57,0xf1,0x00,0x00, // 176.126.87.241
0x01,0x04,0xd4,0x38,0x29,0x3f,0x55,0x08, // 212.56.41.63:21768
0x01,0x04,0xc2,0x8c,0xc6,0xb0,0x55,0x08, // 194.140.198.176:21768
0x01,0x04,0xd4,0x38,0x29,0x2f,0x55,0x08, // 212.56.41.47:21768
0x01,0x04,0x90,0x7e,0x93,0xa5,0x55,0x08, // 144.126.147.165:21768
0x01,0x04,0xb0,0x7e,0x57,0xf1,0x55,0x08, // 176.126.87.241:21768
0x01,0x04,0x0d,0x8c,0x3a,0xfb,0x55,0x08, // 13.140.58.251:21768
0x01,0x04,0x05,0x68,0x53,0x64,0x55,0x08, // 5.104.83.100:21768
};
static const uint8_t chainparams_seed_test[] = {
0x01,0x04,0x01,0x02,0x03,0x04,0x00,0x00, // 1.2.3.4
};
#endif // HUSH_CHAINPARAMSSEEDS_H
#endif // DRAGONX_CHAINPARAMSSEEDS_H

View File

@@ -29,7 +29,7 @@
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
// Must be kept in sync with configure.ac , ugh!
#define CLIENT_VERSION_MAJOR 1
#define CLIENT_VERSION_MINOR 1
#define CLIENT_VERSION_MINOR 3
#define CLIENT_VERSION_REVISION 0
#define CLIENT_VERSION_BUILD 50

View File

@@ -214,19 +214,6 @@ void CCoinsViewCache::AbstractPushAnchor(
}
}
//TODO: delete
/*
template<> void CCoinsViewCache::PushAnchor(const SproutMerkleTree &tree)
{
AbstractPushAnchor<SproutMerkleTree, CAnchorsSproutMap, CAnchorsSproutMap::iterator, CAnchorsSproutCacheEntry>(
tree,
SPROUT,
cacheSproutAnchors,
hashSproutAnchor
);
}
*/
template<> void CCoinsViewCache::PushAnchor(const SaplingMerkleTree &tree)
{
AbstractPushAnchor<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingMap::iterator, CAnchorsSaplingCacheEntry>(
@@ -406,8 +393,6 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
void BatchWriteNullifiers(CNullifiersMap &mapNullifiers, CNullifiersMap &cacheNullifiers)
{
//if(fZdebug)
// LogPrintf("%s\n", __FUNCTION__);
for (CNullifiersMap::iterator child_it = mapNullifiers.begin(); child_it != mapNullifiers.end();) {
if (child_it->second.flags & CNullifiersCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
CNullifiersMap::iterator parent_it = cacheNullifiers.find(child_it->first);
@@ -531,10 +516,7 @@ unsigned int CCoinsViewCache::GetCacheSize() const {
const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const
{
const CCoins* coins = AccessCoins(input.prevout.hash);
//fprintf(stderr, "GetOutputFor: input=%s", input.ToString().c_str());
//fprintf(stderr, "GetOutputFor: prevout n=%d,txid=%s\n", input.prevout.n, input.prevout.hash.ToString().c_str());
assert(coins && coins->IsAvailable(input.prevout.n));
//fprintf(stderr, "GetOutputFor: IsAvailable\n");
return coins->vout[input.prevout.n];
}
@@ -596,7 +578,6 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins* coins = AccessCoins(prevout.hash);
if (!coins || !coins->IsAvailable(prevout.n)) {
//fprintf(stderr,"HaveInputs missing input %s/v%d\n",prevout.hash.ToString().c_str(),prevout.n);
return false;
}
}

View File

@@ -63,10 +63,6 @@ UpgradeState NetworkUpgradeState(
const Consensus::Params& params,
Consensus::UpgradeIndex idx)
{
if (nHeight < 0)
{
printf("height: %d", nHeight);
}
assert(nHeight >= 0);
assert(idx >= Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES);
auto nActivationHeight = params.vUpgrades[idx].nActivationHeight;

View File

@@ -41,7 +41,9 @@
// XXX: There are potential crashes wherever we access chainActive without a lock,
// because it might be disconnecting blocks at the same time.
// TODO: this assumes a blocktime of 75 seconds for HUSH and 60 seconds for other chains
int NOTARISATION_SCAN_LIMIT_BLOCKS = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? 1152 : 1440;
// DragonX: the HUSH3 (1152) branch is dead — SMART_CHAIN_SYMBOL is always "DRAGONX", and at
// static-init time it is empty, so this already always resolved to 1440. Pinned to 1440.
int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440;
CBlockIndex *hush_getblockindex(uint256 hash);
/* On HUSH */

View File

@@ -0,0 +1,98 @@
// Copyright (c) 2016-2026 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
//
// Regression coverage for the b3e81f1ed fix:
// "stratum: do not abort the daemon on a malformed 63-character job_id"
//
// The EWBF "31 bytes job_id" path in stratum_mining_submit() completes a
// 63-character job_id with each hex digit in turn and feeds the result to
// uint256(). ParseHex() stops at the first non-hex character and returns a
// SHORT vector without signalling an error, and base_blob's vector ctor
// asserts vch.size() == 32. asserts are live in release builds here, and the
// job_id arrives from an unauthenticated client -- so a single mining.submit
// whose 63-character job_id contains any non-hex byte (63 spaces will do)
// used to abort the node.
//
// The fix size-checks each candidate before constructing uint256. These tests
// pin the exact invariant that guard relies on, using the real ParseHex and
// uint256 primitives, without ever constructing a uint256 from a short vector
// (which would still abort under the guard we are protecting).
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include "uint256.h"
#include "utilstrencodings.h"
namespace {
const std::string HEXDIGITS = "0123456789abcdef";
// Mirrors the guarded completion loop in stratum_mining_submit(): try every
// single-hex-digit completion and report whether ANY of them parses to a
// whole 32-byte value. Only then is uint256() construction reached.
bool AnyCompletionParsesTo32(const std::string& jobid) {
for (char d : HEXDIGITS) {
std::vector<unsigned char> vch = ParseHex(jobid + d);
if (vch.size() == 32) return true;
}
return false;
}
} // namespace
// The exact example from the fix commit: a 63-character job_id of spaces.
// No completion may reach a 32-byte vector, so the daemon never constructs
// uint256() and never aborts.
TEST(StratumJobId, AllSpacesNeverYields32Bytes) {
std::string spaces(63, ' ');
ASSERT_EQ(spaces.size(), 63u);
for (char d : HEXDIGITS) {
EXPECT_NE(ParseHex(spaces + d).size(), 32u)
<< "completion '" << d << "' unexpectedly produced 32 bytes";
}
EXPECT_FALSE(AnyCompletionParsesTo32(spaces));
}
// A single non-hex byte embedded in an otherwise-hex 63-char job_id is enough:
// ParseHex stops at it, so every completion is short.
TEST(StratumJobId, SingleNonHexByteInMiddleIsRejected) {
std::string jobid(63, 'a');
jobid[30] = 'g'; // 'g' is not a hex digit
ASSERT_EQ(jobid.size(), 63u);
EXPECT_FALSE(AnyCompletionParsesTo32(jobid));
}
// A non-hex byte at the very end (position 62) is likewise rejected: the last
// hex pair can never complete to a whole byte.
TEST(StratumJobId, NonHexByteAtEndIsRejected) {
std::string jobid(62, 'a');
jobid.push_back('z'); // length 63, last char non-hex
ASSERT_EQ(jobid.size(), 63u);
EXPECT_FALSE(AnyCompletionParsesTo32(jobid));
}
// A genuine truncated EWBF job_id -- 63 real hex characters -- must complete
// to exactly 32 bytes for every digit, so uint256() construction is safe.
TEST(StratumJobId, ValidSixtyThreeHexCompletesToExactly32Bytes) {
std::string jobid(63, 'a');
ASSERT_EQ(jobid.size(), 63u);
for (char d : HEXDIGITS) {
std::vector<unsigned char> vch = ParseHex(jobid + d);
ASSERT_EQ(vch.size(), 32u) << "completion '" << d << "'";
// 32-byte vector: construction must not trip the size assertion.
uint256 h(vch);
EXPECT_EQ(h.size(), 32u);
}
EXPECT_TRUE(AnyCompletionParsesTo32(jobid));
}
// Sanity anchors for ParseHex's odd/even handling that the loop depends on:
// an odd hex length drops the trailing nibble (63 hex -> 31 bytes), and one
// more hex char fills the 32nd byte (64 hex -> 32 bytes).
TEST(StratumJobId, ParseHexOddLengthDropsTrailingNibble) {
EXPECT_EQ(ParseHex(std::string(63, 'a')).size(), 31u);
EXPECT_EQ(ParseHex(std::string(64, 'a')).size(), 32u);
}

View File

@@ -95,7 +95,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
errs++;
else
{
//printf("updated %d pubkeys at %s ht.%d\n",num,symbol,ht);
if ( (HUSH_EXTERNAL_NOTARIES != 0 && matched != 0) )
hush_eventadd_pubkeys(sp,symbol,ht,num,pubkeys);
}
@@ -131,7 +130,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
uint8_t n,nid; uint256 hash; uint64_t mask;
n = fgetc(fp);
nid = fgetc(fp);
//printf("U %d %d\n",n,nid);
if ( fread(&mask,1,sizeof(mask),fp) != sizeof(mask) )
errs++;
if ( fread(&hash,1,sizeof(hash),fp) != sizeof(hash) )
@@ -145,7 +143,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
if ( fread(&kheight,1,sizeof(kheight),fp) != sizeof(kheight) )
errs++;
//if ( matched != 0 ) global independent states -> inside *sp
//printf("%s.%d load[%s] ht.%d\n",SMART_CHAIN_SYMBOL,ht,symbol,kheight);
hush_eventadd_hushheight(sp,symbol,ht,kheight,0);
}
else if ( func == 'T' )
@@ -156,7 +153,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
if ( fread(&ktimestamp,1,sizeof(ktimestamp),fp) != sizeof(ktimestamp) )
errs++;
//if ( matched != 0 ) global independent states -> inside *sp
//printf("%s.%d load[%s] ht.%d t.%u\n",SMART_CHAIN_SYMBOL,ht,symbol,kheight,ktimestamp);
hush_eventadd_hushheight(sp,symbol,ht,kheight,ktimestamp);
}
else if ( func == 'R' )
@@ -186,7 +182,6 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
int32_t i;
for (i=0; i<olen; i++)
fgetc(fp);
//printf("illegal olen.%u\n",olen);
}
}
else if ( func == 'D' )
@@ -200,9 +195,7 @@ int32_t hush_parsestatefile(struct hush_state *sp,FILE *fp,char *symbol,char *de
if ( numpvals*sizeof(uint32_t) <= sizeof(pvals) && fread(pvals,sizeof(uint32_t),numpvals,fp) == numpvals )
{
//if ( matched != 0 ) global shared state -> global PVALS
//printf("%s load[%s] prices %d\n",SMART_CHAIN_SYMBOL,symbol,ht);
hush_eventadd_pricefeed(sp,symbol,ht,pvals,numpvals);
//printf("load pvals ht.%d numpvals.%d\n",ht,numpvals);
} else printf("error loading pvals[%d]\n",numpvals);
} // else printf("[%s] %s illegal func.(%d %c)\n",SMART_CHAIN_SYMBOL,symbol,func,func);
return(func);
@@ -239,7 +232,6 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
errs++;
else
{
//printf("updated %d pubkeys at %s ht.%d\n",num,symbol,ht);
if ( (HUSH_EXTERNAL_NOTARIES != 0 && matched != 0) )
hush_eventadd_pubkeys(sp,symbol,ht,num,pubkeys);
}
@@ -274,7 +266,6 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
uint8_t n,nid; uint256 hash; uint64_t mask;
n = filedata[fpos++];
nid = filedata[fpos++];
//printf("U %d %d\n",n,nid);
if ( memread(&mask,sizeof(mask),filedata,&fpos,datalen) != sizeof(mask) )
errs++;
if ( memread(&hash,sizeof(hash),filedata,&fpos,datalen) != sizeof(hash) )
@@ -295,7 +286,6 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
if ( memread(&ktimestamp,sizeof(ktimestamp),filedata,&fpos,datalen) != sizeof(ktimestamp) )
errs++;
//if ( matched != 0 ) global independent states -> inside *sp
//printf("%s.%d load[%s] ht.%d t.%u\n",SMART_CHAIN_SYMBOL,ht,symbol,kheight,ktimestamp);
hush_eventadd_hushheight(sp,symbol,ht,kheight,ktimestamp);
}
else if ( func == 'R' )
@@ -325,7 +315,6 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
int32_t i;
for (i=0; i<olen; i++)
filedata[fpos++];
//printf("illegal olen.%u\n",olen);
}
}
else if ( func == 'D' )
@@ -339,9 +328,7 @@ int32_t hush_parsestatefiledata(struct hush_state *sp,uint8_t *filedata,long *fp
if ( numpvals*sizeof(uint32_t) <= sizeof(pvals) && memread(pvals,(int32_t)(sizeof(uint32_t)*numpvals),filedata,&fpos,datalen) == numpvals*sizeof(uint32_t) )
{
//if ( matched != 0 ) global shared state -> global PVALS
//printf("%s load[%s] prices %d\n",SMART_CHAIN_SYMBOL,symbol,ht);
hush_eventadd_pricefeed(sp,symbol,ht,pvals,numpvals);
//printf("load pvals ht.%d numpvals.%d\n",ht,numpvals);
} else printf("error loading pvals[%d]\n",numpvals);
} // else printf("[%s] %s illegal func.(%d %c)\n",SMART_CHAIN_SYMBOL,symbol,func,func);
*fposp = fpos;
@@ -366,7 +353,6 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
printf("[%s] no hush_stateptr\n",SMART_CHAIN_SYMBOL);
return;
}
//printf("[%s] (%s) -> (%s)\n",SMART_CHAIN_SYMBOL,symbol,dest);
if ( fp == 0 )
{
hush_statefname(fname,SMART_CHAIN_SYMBOL,(char *)"hushstate");
@@ -385,12 +371,10 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
}
if ( height <= 0 )
{
//printf("early return: stateupdate height.%d\n",height);
return;
}
if ( fp != 0 ) // write out funcid, height, other fields, call side effect function
{
//printf("fpos.%ld ",ftell(fp));
if ( HUSHheight != 0 )
{
if ( HUSHtimestamp != 0 )
@@ -425,7 +409,6 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
errs++;
if ( fwrite(opretbuf,1,olen,fp) != olen )
errs++;
//printf("create ht.%d R opret[%d] sp.%p\n",height,olen,sp);
hush_eventadd_opreturn(sp,symbol,height,txhash,opretvalue,vout,opretbuf,olen);
}
else if ( notarypubs != 0 && numnotaries > 0 )
@@ -441,7 +424,6 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
}
else if ( voutmask != 0 && numvouts > 0 )
{
//printf("ht.%d func U %d %d errs.%d hashsize.%ld\n",height,numvouts,notaryid,errs,sizeof(txhash));
fputc('U',fp);
if ( fwrite(&height,1,sizeof(height),fp) != sizeof(height) )
errs++;
@@ -468,13 +450,10 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
if ( fwrite(pvals,sizeof(uint32_t),numpvals,fp) != numpvals )
errs++;
hush_eventadd_pricefeed(sp,symbol,height,pvals,numpvals);
//printf("ht.%d V numpvals[%d]\n",height,numpvals);
}
//printf("save pvals height.%d numpvals.%d\n",height,numpvals);
}
else if ( height != 0 )
{
//printf("ht.%d func N ht.%d errs.%d\n",height,NOTARIZED_HEIGHT,errs);
if ( sp != 0 )
{
if ( sp->MoMdepth != 0 && sp->MoM != zero )
@@ -504,7 +483,6 @@ void hush_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotarie
int32_t hush_validate_chain(uint256 srchash,int32_t notarized_height)
{
//fprintf(stderr,"%s\n", __func__);
static int32_t last_rewind; int32_t rewindtarget; CBlockIndex *pindex; struct hush_state *sp; char symbol[HUSH_SMART_CHAIN_MAXLEN],dest[HUSH_SMART_CHAIN_MAXLEN];
if ( (sp= hush_stateptr(symbol,dest)) == 0 )
return(0);
@@ -555,11 +533,9 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
if ( memcmp(crypto555,scriptbuf+1,33) == 0 )
{
*specialtxp = 1;
//printf(">>>>>>>> ");
}
else if ( hush_chosennotary(&nid,height,scriptbuf + 1,timestamp) >= 0 )
{
//printf("found notary.k%d\n",k);
if ( notaryid < 64 )
{
if ( notaryid < 0 )
@@ -569,9 +545,6 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
}
else if ( notaryid != nid )
{
//for (i=0; i<33; i++)
// printf("%02x",scriptbuf[i+1]);
//printf(" %s mismatch notaryid.%d k.%d\n",SMART_CHAIN_SYMBOL,notaryid,nid);
notaryid = 64;
*voutmaskp = 0;
}
@@ -605,7 +578,6 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
} else {
if ( scriptbuf[len] == 'K' )
{
//fprintf(stderr,"i.%d j.%d KV OPRET len.%d %.8f\n",i,j,opretlen,dstr(value));
hush_stateupdate(height,0,0,0,txhash,0,0,0,0,0,0,value,&scriptbuf[len],opretlen,j,zero,0);
return(-1);
}
@@ -727,9 +699,6 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
}
else if ( matched != 0 )
{
//int32_t k; for (k=0; k<scriptlen; k++)
// printf("%02x",scriptbuf[k]);
//printf(" <- script ht.%d i.%d j.%d value %.8f %s\n",height,i,j,dstr(value),SMART_CHAIN_SYMBOL);
if ( opretlen >= 32*2+4 && strcmp(SMART_CHAIN_SYMBOL,(char *)&scriptbuf[len+32*2+4]) == 0 )
{
for (k=0; k<32; k++)
@@ -793,7 +762,6 @@ int32_t hush_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block)
fprintf(stderr,"unexpected null stateptr.[%s]\n",SMART_CHAIN_SYMBOL);
return(0);
}
//fprintf(stderr,"%s connect.%d\n",SMART_CHAIN_SYMBOL,pindex->nHeight);
// Wallet Filter. Disabled here. Cant be activated by notaries or pools with some changes.
numnotaries = hush_notaries(pubkeys,pindex->GetHeight(),pindex->GetBlockTime());
calc_rmd160_sha256(rmd160,pubkeys[0],33);
@@ -970,7 +938,6 @@ int32_t hush_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block)
else
{ fprintf(stderr,"hush_connectblock: unexpected null pindex\n"); return(0); }
//HUSH_INITDONE = (uint32_t)time(NULL);
//fprintf(stderr,"%s end connect.%d\n",SMART_CHAIN_SYMBOL,pindex->GetHeight());
if (fJustCheck)
{
if ( notarizations.size() == 0 )

View File

@@ -975,8 +975,9 @@ uint64_t hush_commission(int height)
INTERVAL = GetArg("-ac_halving1",840000), TRANSITION = 129;
uint64_t commission = 0;
//TODO: Likely a bug hiding here or at the next halving :)
//if( height >= HALVING1) {
// NB: INTERVAL is consumed only by the debug fprintf at the end of this function;
// the commission schedule below uses hardcoded height thresholds, not INTERVAL. So
// the > vs >= boundary at HALVING1 has no consensus effect. Left as > for stability.
if( height > HALVING1) {
// Block time going from 150s to 75s (half) means the interval between halvings
// must be twice as often, i.e. 840000*2=1680000
@@ -1019,14 +1020,14 @@ uint64_t hush_commission(int height)
commission = 61035;
} else if (height < 23860000) {
commission = 30517;
} else if (height < 23860000) {
commission = 15258;
// removed unreachable duplicate `height < 23860000` (=> 15258); the schedule
// intentionally drops straight to 7629 next — this is the deployed behavior.
} else if (height < 25540000) {
commission = 7629;
} else if (height < 27220000) {
commission = 3814;
} else if (height < 27220000) {
commission = 1907;
// removed unreachable duplicate `height < 27220000` (=> 1907); the schedule
// intentionally drops straight to 953 next — this is the deployed behavior.
} else if (height < 28900000) {
commission = 953;
} else if (height < 30580000) {

View File

@@ -597,7 +597,7 @@ void hush_netevent(std::vector<uint8_t> payload);
int32_t getacseason(uint32_t timestamp);
int32_t gethushseason(int32_t height);
#define DRAGON_MAXSCRIPTSIZE 10001
// DRAGON_MAXSCRIPTSIZE is defined once near the top of this header; the duplicate here was removed.
#define HUSH_KVDURATION 1440
#define HUSH_KVBINARY 2
#define PRICES_SMOOTHWIDTH 1

View File

@@ -568,8 +568,6 @@ uint256 NSPV_opretextract(int32_t *heightp,uint256 *blockhashp,char *symbol,std:
((uint8_t *)blockhashp)[i] = opret[i];
for (i=0; i<32; i++)
((uint8_t *)&desttxid)[i] = opret[4 + 32 + i];
if ( 0 && *heightp != 2690 )
fprintf(stderr," ntzht.%d %s <- txid.%s size.%d\n",*heightp,(*blockhashp).GetHex().c_str(),(txid).GetHex().c_str(),(int32_t)opret.size());
return(desttxid);
}

View File

@@ -38,7 +38,8 @@ struct NSPV_ntzargs
int32_t NSPV_notarization_find(struct NSPV_ntzargs *args,int32_t height,int32_t dir)
{
int32_t ntzheight = 0; uint256 hashBlock; CTransaction tx; Notarization nota; char *symbol; std::vector<uint8_t> opret;
symbol = (SMART_CHAIN_SYMBOL[0] == 0) ? (char *)"HUSH3" : SMART_CHAIN_SYMBOL;
// Notarization symbol; the empty-symbol fallback is dead on DragonX (SMART_CHAIN_SYMBOL is always "DRAGONX", never empty)
symbol = (SMART_CHAIN_SYMBOL[0] == 0) ? (char *)"DRAGONX" : SMART_CHAIN_SYMBOL;
memset(args,0,sizeof(*args));
if ( dir > 0 )
height += 10;
@@ -659,7 +660,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
struct NSPV_utxosresp U;
if ( len >= 2 && len < 64+5 && request[1] < 64 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
{
int32_t skipcount = 0; char coinaddr[64]; uint8_t filter; uint8_t isCC = 0;
int32_t skipcount = 0; char coinaddr[64]; uint32_t filter; uint8_t isCC = 0;
memcpy(coinaddr,&request[2],request[1]); // request[1] < 64 bounds the copy + the terminator write
coinaddr[request[1]] = 0;
if ( request[1] == len-3 )
@@ -675,8 +676,6 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
dragon_rwnum(0,&request[len-8],sizeof(skipcount),&skipcount);
dragon_rwnum(0,&request[len-4],sizeof(filter),&filter);
}
if ( 0 && isCC != 0 )
fprintf(stderr,"utxos %s isCC.%d skipcount.%d filter.%x\n",coinaddr,isCC,skipcount,filter);
memset(&U,0,sizeof(U));
if ( (slen= NSPV_getaddressutxos(&U,coinaddr,isCC,skipcount,filter)) > 0 )
{
@@ -715,8 +714,6 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
dragon_rwnum(0,&request[len-8],sizeof(skipcount),&skipcount);
dragon_rwnum(0,&request[len-4],sizeof(filter),&filter);
}
if ( 0 && isCC != 0 )
fprintf(stderr,"txids %s isCC.%d skipcount.%d filter.%d\n",coinaddr,isCC,skipcount,filter);
memset(&T,0,sizeof(T));
if ( (slen= NSPV_getaddresstxids(&T,coinaddr,isCC,skipcount,filter)) > 0 )
{

View File

@@ -62,7 +62,7 @@ struct NSPV_ntzsresp *NSPV_ntzsresp_add(struct NSPV_ntzsresp *ptr)
i = (rand() % (sizeof(NSPV_ntzsresp_cache)/sizeof(*NSPV_ntzsresp_cache)));
NSPV_ntzsresp_purge(&NSPV_ntzsresp_cache[i]);
NSPV_ntzsresp_copy(&NSPV_ntzsresp_cache[i],ptr);
fprintf(stderr,"ADD CACHE ntzsresp req.%d\n",ptr->reqheight);
LogPrint("nspv","ADD CACHE ntzsresp req.%d\n",ptr->reqheight);
return(&NSPV_ntzsresp_cache[i]);
}
@@ -101,7 +101,7 @@ struct NSPV_txproof *NSPV_txproof_add(struct NSPV_txproof *ptr)
i = (rand() % (sizeof(NSPV_txproof_cache)/sizeof(*NSPV_txproof_cache)));
NSPV_txproof_purge(&NSPV_txproof_cache[i]);
NSPV_txproof_copy(&NSPV_txproof_cache[i],ptr);
fprintf(stderr,"ADD CACHE txproof %s\n",ptr->txid.GetHex().c_str());
LogPrint("nspv","ADD CACHE txproof %s\n",ptr->txid.GetHex().c_str());
return(&NSPV_txproof_cache[i]);
}
@@ -124,7 +124,7 @@ struct NSPV_ntzsproofresp *NSPV_ntzsproof_add(struct NSPV_ntzsproofresp *ptr)
i = (rand() % (sizeof(NSPV_ntzsproofresp_cache)/sizeof(*NSPV_ntzsproofresp_cache)));
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresp_cache[i]);
NSPV_ntzsproofresp_copy(&NSPV_ntzsproofresp_cache[i],ptr);
fprintf(stderr,"ADD CACHE ntzsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str());
LogPrint("nspv","ADD CACHE ntzsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str());
return(&NSPV_ntzsproofresp_cache[i]);
}
@@ -139,13 +139,13 @@ void hush_nSPVresp(CNode *pfrom,std::vector<uint8_t> response) // received a res
switch ( response[0] )
{
case NSPV_INFORESP:
fprintf(stderr,"got version.%d info response %u size.%d height.%d\n",NSPV_inforesult.version,timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
LogPrint("nspv","got version.%d info response %u size.%d height.%d\n",NSPV_inforesult.version,timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
I = NSPV_inforesult;
NSPV_inforesp_purge(&NSPV_inforesult);
NSPV_rwinforesp(0,&response[1],&NSPV_inforesult);
if ( NSPV_inforesult.height < I.height )
{
fprintf(stderr,"got old info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
LogPrint("nspv","got old info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
NSPV_inforesp_purge(&NSPV_inforesult);
NSPV_inforesult = I;
}
@@ -160,56 +160,56 @@ void hush_nSPVresp(CNode *pfrom,std::vector<uint8_t> response) // received a res
case NSPV_UTXOSRESP:
NSPV_utxosresp_purge(&NSPV_utxosresult);
NSPV_rwutxosresp(0,&response[1],&NSPV_utxosresult);
fprintf(stderr,"got utxos response %u size.%d\n",timestamp,(int32_t)response.size());
LogPrint("nspv","got utxos response %u size.%d\n",timestamp,(int32_t)response.size());
break;
case NSPV_TXIDSRESP:
NSPV_txidsresp_purge(&NSPV_txidsresult);
NSPV_rwtxidsresp(0,&response[1],&NSPV_txidsresult);
fprintf(stderr,"got txids response %u size.%d %s CC.%d num.%d\n",timestamp,(int32_t)response.size(),NSPV_txidsresult.coinaddr,NSPV_txidsresult.CCflag,NSPV_txidsresult.numtxids);
LogPrint("nspv","got txids response %u size.%d %s CC.%d num.%d\n",timestamp,(int32_t)response.size(),NSPV_txidsresult.coinaddr,NSPV_txidsresult.CCflag,NSPV_txidsresult.numtxids);
break;
case NSPV_MEMPOOLRESP:
NSPV_mempoolresp_purge(&NSPV_mempoolresult);
NSPV_rwmempoolresp(0,&response[1],&NSPV_mempoolresult);
fprintf(stderr,"got mempool response %u size.%d %s CC.%d num.%d funcid.%d %s/v%d\n",timestamp,(int32_t)response.size(),NSPV_mempoolresult.coinaddr,NSPV_mempoolresult.CCflag,NSPV_mempoolresult.numtxids,NSPV_mempoolresult.funcid,NSPV_mempoolresult.txid.GetHex().c_str(),NSPV_mempoolresult.vout);
LogPrint("nspv","got mempool response %u size.%d %s CC.%d num.%d funcid.%d %s/v%d\n",timestamp,(int32_t)response.size(),NSPV_mempoolresult.coinaddr,NSPV_mempoolresult.CCflag,NSPV_mempoolresult.numtxids,NSPV_mempoolresult.funcid,NSPV_mempoolresult.txid.GetHex().c_str(),NSPV_mempoolresult.vout);
break;
case NSPV_NTZSRESP:
NSPV_ntzsresp_purge(&NSPV_ntzsresult);
NSPV_rwntzsresp(0,&response[1],&NSPV_ntzsresult);
if ( NSPV_ntzsresp_find(NSPV_ntzsresult.reqheight) == 0 )
NSPV_ntzsresp_add(&NSPV_ntzsresult);
fprintf(stderr,"got ntzs response %u size.%d %s prev.%d, %s next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.prevntz.height,NSPV_ntzsresult.nextntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.height);
LogPrint("nspv","got ntzs response %u size.%d %s prev.%d, %s next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.prevntz.height,NSPV_ntzsresult.nextntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.height);
break;
case NSPV_NTZSPROOFRESP:
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult);
NSPV_rwntzsproofresp(0,&response[1],&NSPV_ntzsproofresult);
if ( NSPV_ntzsproof_find(NSPV_ntzsproofresult.prevtxid,NSPV_ntzsproofresult.nexttxid) == 0 )
NSPV_ntzsproof_add(&NSPV_ntzsproofresult);
fprintf(stderr,"got ntzproof response %u size.%d prev.%d next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsproofresult.common.prevht,NSPV_ntzsproofresult.common.nextht);
LogPrint("nspv","got ntzproof response %u size.%d prev.%d next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsproofresult.common.prevht,NSPV_ntzsproofresult.common.nextht);
break;
case NSPV_TXPROOFRESP:
NSPV_txproof_purge(&NSPV_txproofresult);
NSPV_rwtxproof(0,&response[1],&NSPV_txproofresult);
if ( NSPV_txproof_find(NSPV_txproofresult.txid) == 0 )
NSPV_txproof_add(&NSPV_txproofresult);
fprintf(stderr,"got txproof response %u size.%d %s ht.%d\n",timestamp,(int32_t)response.size(),NSPV_txproofresult.txid.GetHex().c_str(),NSPV_txproofresult.height);
LogPrint("nspv","got txproof response %u size.%d %s ht.%d\n",timestamp,(int32_t)response.size(),NSPV_txproofresult.txid.GetHex().c_str(),NSPV_txproofresult.height);
break;
case NSPV_SPENTINFORESP:
NSPV_spentinfo_purge(&NSPV_spentresult);
NSPV_rwspentinfo(0,&response[1],&NSPV_spentresult);
fprintf(stderr,"got spentinfo response %u size.%d\n",timestamp,(int32_t)response.size());
LogPrint("nspv","got spentinfo response %u size.%d\n",timestamp,(int32_t)response.size());
break;
case NSPV_BROADCASTRESP:
NSPV_broadcast_purge(&NSPV_broadcastresult);
NSPV_rwbroadcastresp(0,&response[1],&NSPV_broadcastresult);
fprintf(stderr,"got broadcast response %u size.%d %s retcode.%d\n",timestamp,(int32_t)response.size(),NSPV_broadcastresult.txid.GetHex().c_str(),NSPV_broadcastresult.retcode);
LogPrint("nspv","got broadcast response %u size.%d %s retcode.%d\n",timestamp,(int32_t)response.size(),NSPV_broadcastresult.txid.GetHex().c_str(),NSPV_broadcastresult.retcode);
break;
case NSPV_CCMODULEUTXOSRESP:
NSPV_utxosresp_purge(&NSPV_utxosresult);
NSPV_rwutxosresp(0, &response[1], &NSPV_utxosresult);
fprintf(stderr, "got cc module utxos response %u size.%d\n", timestamp, (int32_t)response.size());
LogPrint("nspv", "got cc module utxos response %u size.%d\n", timestamp, (int32_t)response.size());
break;
default: fprintf(stderr,"unexpected response %02x size.%d at %u\n",response[0],(int32_t)response.size(),timestamp);
default: LogPrint("nspv","unexpected response %02x size.%d at %u\n",response[0],(int32_t)response.size(),timestamp);
break;
}
}
@@ -254,7 +254,7 @@ CNode *NSPV_req(CNode *pnode,uint8_t *msg,int32_t len,uint64_t mask,int32_t ind)
pnode->PushMessage("getnSPV",request);
pnode->prevtimes[ind] = timestamp;
return(pnode);
} else fprintf(stderr,"no pnodes\n");
} else LogPrint("nspv","no pnodes\n");
return(0);
}
@@ -263,7 +263,7 @@ UniValue NSPV_logout()
UniValue result(UniValue::VOBJ);
result.push_back(Pair("result","success"));
if ( NSPV_logintime != 0 )
fprintf(stderr,"scrub wif and privkey from NSPV memory\n");
LogPrint("nspv","scrub wif and privkey from NSPV memory\n");
else result.push_back(Pair("status","wasnt logged in"));
memset(NSPV_ntzsproofresp_cache,0,sizeof(NSPV_ntzsproofresp_cache));
memset(NSPV_txproof_cache,0,sizeof(NSPV_txproof_cache));
@@ -294,7 +294,6 @@ void hush_nSPV(CNode *pto) // polling loop from SendMessages
len = 0;
msg[len++] = NSPV_INFO;
len += dragon_rwnum(1,&msg[len],sizeof(reqht),&reqht);
//fprintf(stderr,"issue getinfo\n");
NSPV_req(pto,msg,len,NODE_NSPV,NSPV_INFO>>1);
}
}
@@ -485,7 +484,6 @@ UniValue NSPV_ntzsproof_json(struct NSPV_ntzsproofresp *ptr)
result.push_back(Pair("numhdrs",(int64_t)ptr->common.numhdrs));
result.push_back(Pair("headers",NSPV_headers_json(ptr->common.hdrs,ptr->common.numhdrs,ptr->common.prevht)));
result.push_back(Pair("lastpeer",NSPV_lastpeer));
//fprintf(stderr,"ntzs_proof %s %d, %s %d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht);
return(result);
}
@@ -577,7 +575,7 @@ uint32_t NSPV_blocktime(int32_t hdrheight)
{
timestamp = NSPV_inforesult.H.nTime;
NSPV_inforesult = old;
fprintf(stderr,"NSPV_blocktime ht.%d -> t%u\n",hdrheight,timestamp);
LogPrint("nspv","NSPV_blocktime ht.%d -> t%u\n",hdrheight,timestamp);
return(timestamp);
}
}
@@ -588,7 +586,6 @@ uint32_t NSPV_blocktime(int32_t hdrheight)
UniValue NSPV_addressutxos(char *coinaddr,int32_t CCflag,int32_t skipcount,int32_t filter)
{
UniValue result(UniValue::VOBJ); uint8_t msg[512]; int32_t i,iter,slen,len = 0;
//fprintf(stderr,"utxos %s NSPV addr %s\n",coinaddr,NSPV_address.c_str());
//if ( NSPV_utxosresult.nodeheight >= NSPV_inforesult.height && strcmp(coinaddr,NSPV_utxosresult.coinaddr) == 0 && CCflag == NSPV_utxosresult.CCflag && skipcount == NSPV_utxosresult.skipcount && filter == NSPV_utxosresult.filter )
// return(NSPV_utxosresp_json(&NSPV_utxosresult));
if ( skipcount < 0 )
@@ -644,7 +641,6 @@ UniValue NSPV_addresstxids(char *coinaddr,int32_t CCflag,int32_t skipcount,int32
msg[len++] = (CCflag != 0);
len += dragon_rwnum(1,&msg[len],sizeof(skipcount),&skipcount);
len += dragon_rwnum(1,&msg[len],sizeof(filter),&filter);
//fprintf(stderr,"skipcount.%d\n",skipcount);
for (iter=0; iter<3; iter++)
if ( NSPV_req(0,msg,len,NODE_ADDRINDEX,msg[0]>>1) != 0 )
{
@@ -683,7 +679,7 @@ UniValue NSPV_ccaddresstxids(char *coinaddr,int32_t CCflag,int32_t skipcount,uin
slen = (int32_t)strlen(coinaddr);
msg[len++] = slen;
memcpy(&msg[len],coinaddr,slen), len += slen;
fprintf(stderr,"(%s) func.%d CC.%d %s skipcount.%d len.%d\n",coinaddr,NSPV_CC_TXIDS,CCflag,filtertxid.GetHex().c_str(),skipcount,len);
LogPrint("nspv","(%s) func.%d CC.%d %s skipcount.%d len.%d\n",coinaddr,NSPV_CC_TXIDS,CCflag,filtertxid.GetHex().c_str(),skipcount,len);
for (iter=0; iter<3; iter++)
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 )
{
@@ -721,7 +717,7 @@ UniValue NSPV_mempooltxids(char *coinaddr,int32_t CCflag,uint8_t funcid,uint256
slen = (int32_t)strlen(coinaddr);
msg[len++] = slen;
memcpy(&msg[len],coinaddr,slen), len += slen;
fprintf(stderr,"(%s) func.%d CC.%d %s/v%d len.%d\n",coinaddr,funcid,CCflag,txid.GetHex().c_str(),vout,len);
LogPrint("nspv","(%s) func.%d CC.%d %s/v%d len.%d\n",coinaddr,funcid,CCflag,txid.GetHex().c_str(),vout,len);
for (iter=0; iter<3; iter++)
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 )
{
@@ -782,7 +778,7 @@ UniValue NSPV_notarizations(int32_t reqheight)
uint8_t msg[512]; int32_t i,iter,len = 0; struct NSPV_ntzsresp N,*ptr;
if ( (ptr= NSPV_ntzsresp_find(reqheight)) != 0 )
{
fprintf(stderr,"FROM CACHE NSPV_notarizations.%d\n",reqheight);
LogPrint("nspv","FROM CACHE NSPV_notarizations.%d\n",reqheight);
NSPV_ntzsresp_purge(&NSPV_ntzsresult);
NSPV_ntzsresp_copy(&NSPV_ntzsresult,ptr);
return(NSPV_ntzsresp_json(ptr));
@@ -808,7 +804,7 @@ UniValue NSPV_txidhdrsproof(uint256 prevtxid,uint256 nexttxid)
uint8_t msg[512]; int32_t i,iter,len = 0; struct NSPV_ntzsproofresp P,*ptr;
if ( (ptr= NSPV_ntzsproof_find(prevtxid,nexttxid)) != 0 )
{
fprintf(stderr,"FROM CACHE NSPV_txidhdrsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str());
LogPrint("nspv","FROM CACHE NSPV_txidhdrsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str());
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult);
NSPV_ntzsproofresp_copy(&NSPV_ntzsproofresult,ptr);
return(NSPV_ntzsproof_json(ptr));
@@ -846,7 +842,7 @@ UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height)
uint8_t msg[512]; int32_t i,iter,len = 0; struct NSPV_txproof P,*ptr;
if ( (ptr= NSPV_txproof_find(txid)) != 0 )
{
fprintf(stderr,"FROM CACHE NSPV_txproof %s\n",txid.GetHex().c_str());
LogPrint("nspv","FROM CACHE NSPV_txproof %s\n",txid.GetHex().c_str());
NSPV_txproof_purge(&NSPV_txproofresult);
NSPV_txproof_copy(&NSPV_txproofresult,ptr);
return(NSPV_txproof_json(ptr));
@@ -856,7 +852,7 @@ UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height)
len += dragon_rwnum(1,&msg[len],sizeof(height),&height);
len += dragon_rwnum(1,&msg[len],sizeof(vout),&vout);
len += dragon_rwbignum(1,&msg[len],sizeof(txid),(uint8_t *)&txid);
fprintf(stderr,"req txproof %s/v%d at height.%d\n",txid.GetHex().c_str(),vout,height);
LogPrint("nspv","req txproof %s/v%d at height.%d\n",txid.GetHex().c_str(),vout,height);
for (iter=0; iter<3; iter++)
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 )
{
@@ -867,7 +863,7 @@ UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height)
return(NSPV_txproof_json(&NSPV_txproofresult));
}
} else sleep(1);
fprintf(stderr,"txproof timeout\n");
LogPrint("nspv","txproof timeout\n");
memset(&P,0,sizeof(P));
return(NSPV_txproof_json(&P));
}
@@ -907,7 +903,6 @@ UniValue NSPV_broadcast(char *hex)
len += dragon_rwnum(1,&msg[len],sizeof(n),&n);
memcpy(&msg[len],data,n), len += n;
free(data);
//fprintf(stderr,"send txid.%s\n",txid.GetHex().c_str());
for (iter=0; iter<3; iter++)
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 )
{

View File

@@ -26,7 +26,7 @@ int32_t NSPV_validatehdrs(struct NSPV_ntzsproofresp *ptr)
int32_t i,height,txidht; CTransaction tx; uint256 blockhash,txid,desttxid;
if ( (ptr->common.nextht-ptr->common.prevht+1) != ptr->common.numhdrs )
{
fprintf(stderr,"next.%d prev.%d -> %d vs %d\n",ptr->common.nextht,ptr->common.prevht,ptr->common.nextht-ptr->common.prevht+1,ptr->common.numhdrs);
LogPrintf("next.%d prev.%d -> %d vs %d\n",ptr->common.nextht,ptr->common.prevht,ptr->common.nextht-ptr->common.prevht+1,ptr->common.numhdrs);
return(-2);
}
else if ( NSPV_txextract(tx,ptr->nextntz,ptr->nexttxlen) < 0 )
@@ -64,7 +64,6 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
struct NSPV_txproof *ptr; int32_t i,offset,retval; int64_t rewards = 0; uint32_t nLockTime; std::vector<uint8_t> proof;
retval = skipvalidation != 0 ? 0 : -1;
//fprintf(stderr,"NSPV_gettx %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height);
if ( (ptr= NSPV_txproof_find(txid)) == 0 )
{
NSPV_txproof(vout,txid,height);
@@ -75,7 +74,7 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
currentheight=NSPV_inforesult.height;
if ( ptr->txid != txid )
{
fprintf(stderr,"txproof error %s != %s\n",ptr->txid.GetHex().c_str(),txid.GetHex().c_str());
LogPrintf("txproof error %s != %s\n",ptr->txid.GetHex().c_str(),txid.GetHex().c_str());
return(-1);
}
else if ( NSPV_txextract(tx,ptr->tx,ptr->txlen) < 0 || ptr->txlen <= 0 )
@@ -87,8 +86,7 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
//char coinaddr[64];
//Getscriptaddress(coinaddr,tx.vout[0].scriptPubKey); causes crash??
//fprintf(stderr,"%s txid.%s vs hash.%s\n",coinaddr,txid.GetHex().c_str(),tx.GetHash().GetHex().c_str());
if ( skipvalidation == 0 )
{
if ( ptr->txprooflen > 0 )
@@ -99,18 +97,17 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
NSPV_notarizations(height); // gets the prev and next notarizations
if ( NSPV_inforesult.notarization.height >= height && (NSPV_ntzsresult.prevntz.height == 0 || NSPV_ntzsresult.prevntz.height >= NSPV_ntzsresult.nextntz.height) )
{
fprintf(stderr,"issue manual bracket\n");
LogPrintf("issue manual bracket\n");
NSPV_notarizations(height-1);
NSPV_notarizations(height+1);
NSPV_notarizations(height); // gets the prev and next notarizations
}
if ( NSPV_ntzsresult.prevntz.height != 0 && NSPV_ntzsresult.prevntz.height <= NSPV_ntzsresult.nextntz.height )
{
fprintf(stderr,">>>>> gettx ht.%d prev.%d next.%d\n",height,NSPV_ntzsresult.prevntz.height, NSPV_ntzsresult.nextntz.height);
LogPrintf(">>>>> gettx ht.%d prev.%d next.%d\n",height,NSPV_ntzsresult.prevntz.height, NSPV_ntzsresult.nextntz.height);
offset = (height - NSPV_ntzsresult.prevntz.height);
if ( offset >= 0 && height <= NSPV_ntzsresult.nextntz.height )
{
//fprintf(stderr,"call NSPV_txidhdrsproof %s %s\n",NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.txid.GetHex().c_str());
NSPV_txidhdrsproof(NSPV_ntzsresult.prevntz.txid,NSPV_ntzsresult.nextntz.txid);
usleep(10000);
if ( (retval= NSPV_validatehdrs(&NSPV_ntzsproofresult)) == 0 )
@@ -119,8 +116,8 @@ int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int
proofroot = BitcoinGetProofMerkleRoot(proof,txids);
if ( proofroot != NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot || txids[0] != txid )
{
fprintf(stderr,"txid.%s vs txids[0] %s\n",txid.GetHex().c_str(),txids[0].GetHex().c_str());
fprintf(stderr,"prooflen.%d proofroot.%s vs %s\n",(int32_t)proof.size(),proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str());
LogPrintf("txid.%s vs txids[0] %s\n",txid.GetHex().c_str(),txids[0].GetHex().c_str());
LogPrintf("prooflen.%d proofroot.%s vs %s\n",(int32_t)proof.size(),proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str());
retval = -2003;
} else retval = 0;
}
@@ -162,13 +159,11 @@ int32_t NSPV_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t
belowi = i;
}
}
//printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below));
}
*aboveip = abovei;
*abovep = above;
*belowip = belowi;
*belowp = below;
//printf("above.%d below.%d\n",abovei,belowi);
if ( abovei >= 0 && belowi >= 0 )
{
if ( above < (below >> 1) )
@@ -195,14 +190,13 @@ int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64
utxos[n++] = ptr[i];
}
remains = total;
//fprintf(stderr,"threshold %.8f n.%d for total %.8f\n",(double)threshold/COIN,n,(double)total/COIN);
for (i=0; i<maxinputs && n>0; i++)
{
below = above = 0;
abovei = belowi = -1;
if ( NSPV_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 )
{
fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN);
LogPrintf("error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN);
return(0);
}
if ( belowi < 0 || abovei >= 0 )
@@ -210,10 +204,9 @@ int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64
else ind = belowi;
if ( ind < 0 )
{
fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind);
LogPrintf("error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind);
return(0);
}
//fprintf(stderr,"i.%d ind.%d abovei.%d belowi.%d n.%d\n",i,ind,abovei,belowi,n);
up = &utxos[ind];
mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript()));
used[i] = *up;
@@ -221,11 +214,9 @@ int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64
remains -= up->satoshis;
utxos[ind] = utxos[--n];
memset(&utxos[n],0,sizeof(utxos[n]));
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
if ( totalinputs >= total || (i+1) >= maxinputs )
break;
}
//fprintf(stderr,"totalinputs %.8f vs total %.8f\n",(double)totalinputs/COIN,(double)total/COIN);
if ( totalinputs >= total )
return(totalinputs);
return(0);
@@ -236,21 +227,20 @@ bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const C
CTransaction txNewConst(mtx); SignatureData sigdata; CBasicKeyStore keystore; int64_t branchid = NSPV_BRANCHID;
if ( NSPV_logintime == 0 || time(NULL) > NSPV_logintime+NSPV_AUTOLOGOUT )
{
fprintf(stderr,"need to be logged in to get myprivkey\n");
LogPrintf("need to be logged in to get myprivkey\n");
return false;
}
keystore.AddKey(NSPV_key);
if ( nTime != 0 && nTime < HUSH_SAPING_ACTIVATION )
{
fprintf(stderr,"use legacy sig validation\n");
LogPrintf("use legacy sig validation\n");
branchid = 0;
}
if ( ProduceSignature(TransactionSignatureCreator(&keystore,&txNewConst,vini,utxovalue,SIGHASH_ALL),scriptPubKey,sigdata,branchid) != 0 )
{
UpdateTransaction(mtx,vini,sigdata);
fprintf(stderr,"SIG_TXHASH %s vini.%d %.8f\n",SIG_TXHASH.GetHex().c_str(),vini,(double)utxovalue/COIN);
return(true);
} //else fprintf(stderr,"sigerr SIG_TXHASH %s vini.%d %.8f\n",SIG_TXHASH.GetHex().c_str(),vini,(double)utxovalue/COIN);
}
return(false);
}
@@ -285,22 +275,21 @@ std::string NSPV_signtx(int64_t &rewardsum,int64_t &interestsum,UniValue &retcod
{
if ( vintx.vout[utxovout].nValue != used[i].satoshis )
{
fprintf(stderr,"vintx mismatch %.8f != %.8f\n",(double)vintx.vout[utxovout].nValue/COIN,(double)used[i].satoshis/COIN);
LogPrintf("vintx mismatch %.8f != %.8f\n",(double)vintx.vout[utxovout].nValue/COIN,(double)used[i].satoshis/COIN);
return("");
}
else if ( utxovout != used[i].vout )
{
fprintf(stderr,"vintx vout mismatch %d != %d\n",utxovout,used[i].vout);
LogPrintf("vintx vout mismatch %d != %d\n",utxovout,used[i].vout);
return("");
}
else if ( NSPV_SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey,0) == 0 )
{
fprintf(stderr,"signing error for vini.%d\n",i);
LogPrintf("signing error for vini.%d\n",i);
return("");
}
} else fprintf(stderr,"couldnt find txid.%s/v%d or it was spent\n",mtx.vin[i].prevout.hash.GetHex().c_str(),utxovout); // of course much better handling is needed
} else LogPrintf("couldnt find txid.%s/v%d or it was spent\n",mtx.vin[i].prevout.hash.GetHex().c_str(),utxovout); // of course much better handling is needed
}
fprintf(stderr,"sign %d inputs %.8f + interest %.8f -> %d outputs %.8f change %.8f\n",(int32_t)mtx.vin.size(),(double)totalinputs/COIN,(double)interest/COIN,(int32_t)mtx.vout.size(),(double)totaloutputs/COIN,(double)change/COIN);
return(EncodeHexTx(mtx));
}
@@ -360,7 +349,6 @@ UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis) // what its a
result.push_back(Pair("amount",(double)satoshis/COIN));
return(result);
}
printf("%s numutxos.%d balance %.8f\n",NSPV_utxosresult.coinaddr,NSPV_utxosresult.numutxos,(double)NSPV_utxosresult.total/COIN);
CScript opret; std::string hex; struct NSPV_utxoresp used[NSPV_MAXVINS]; CMutableTransaction mtx; CTransaction tx; int64_t rewardsum=0,interestsum=0;
mtx.fOverwintered = true;
mtx.nExpiryHeight = 0;
@@ -428,7 +416,7 @@ int64_t NSPV_AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total
NSPV_utxosresp_purge(&ptr->U);
NSPV_utxosresp_copy(&ptr->U,&NSPV_utxosresult);
// }
fprintf(stderr,"%s numutxos.%d\n",ptr->U.coinaddr,ptr->U.numutxos);
LogPrintf("%s numutxos.%d\n",ptr->U.coinaddr,ptr->U.numutxos);
memset(ptr->used,0,sizeof(ptr->used));
return(NSPV_addinputs(ptr->used,mtx,total,maxinputs,ptr->U.utxos,ptr->U.numutxos));
} else return(0);
@@ -442,7 +430,7 @@ void NSPV_utxos2CCunspents(struct NSPV_utxosresp *ptr,std::vector<std::pair<CAdd
CBitcoinAddress address(addrstr);
if ( address.GetIndexKey(hashBytes, type, ptr->CCflag) == 0 )
{
fprintf(stderr,"couldnt get indexkey\n");
LogPrintf("couldnt get indexkey\n");
return;
}
for (i = 0; i < ptr->numutxos; i ++)
@@ -466,7 +454,7 @@ void NSPV_txids2CCtxids(struct NSPV_txidsresp *ptr,std::vector<std::pair<CAddres
CBitcoinAddress address(addrstr);
if ( address.GetIndexKey(hashBytes, type, ptr->CCflag) == 0 )
{
fprintf(stderr,"couldnt get indexkey\n");
LogPrintf("couldnt get indexkey\n");
return;
}
for (i = 0; i < ptr->numtxids; i ++)

View File

@@ -770,19 +770,15 @@ int32_t bitcoin_addr2rmd160(uint8_t *addrtypep,uint8_t rmd160[20],char *coinaddr
memcpy(rmd160,buf+1,20);
if ( (buf[21]&0xff) == hash.bytes[31] && (buf[22]&0xff) == hash.bytes[30] &&(buf[23]&0xff) == hash.bytes[29] && (buf[24]&0xff) == hash.bytes[28] )
{
//printf("coinaddr.(%s) valid checksum addrtype.%02x\n",coinaddr,*addrtypep);
return(20);
}
else
{
int32_t i;
if ( len > 20 )
{
hash = bits256_doublesha256(0,buf,len);
}
for (i=0; i<len; i++)
printf("%02x ",buf[i]);
printf("\nhex checkhash.(%s) len.%d mismatch %02x %02x %02x %02x vs %02x %02x %02x %02x\n",coinaddr,len,buf[len-1]&0xff,buf[len-2]&0xff,buf[len-3]&0xff,buf[len-4]&0xff,hash.bytes[31],hash.bytes[30],hash.bytes[29],hash.bytes[28]);
LogPrintf("\nhex checkhash.(%s) len.%d mismatch %02x %02x %02x %02x vs %02x %02x %02x %02x\n",coinaddr,len,buf[len-1]&0xff,buf[len-2]&0xff,buf[len-3]&0xff,buf[len-4]&0xff,hash.bytes[31],hash.bytes[30],hash.bytes[29],hash.bytes[28]);
}
}
return(0);
@@ -801,10 +797,6 @@ char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,
data[21+i] = hash.bytes[31-i];
if ( (coinaddr= bitcoin_base58encode(coinaddr,data,25)) != 0 )
{
//uint8_t checktype,rmd160[20];
//bitcoin_addr2rmd160(&checktype,rmd160,coinaddr);
//if ( strcmp(checkaddr,coinaddr) != 0 )
// printf("checkaddr.(%s) vs coinaddr.(%s) %02x vs [%02x] memcmp.%d\n",checkaddr,coinaddr,addrtype,checktype,memcmp(rmd160,data+1,20));
}
return(coinaddr);
}
@@ -858,7 +850,7 @@ int32_t unhex(char c)
int32_t hex;
if ( (hex= _unhex(c)) < 0 )
{
fprintf(stderr,"unhex: illegal hexchar.(%c)\n",c);
LogPrintf("unhex: illegal hexchar.(%c)\n",c);
}
return(hex);
}
@@ -868,7 +860,6 @@ unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])
int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex)
{
int32_t adjust,i = 0;
//printf("decode.(%s)\n",hex);
if ( is_hexstr(hex,n) <= 0 )
{
memset(bytes,0,n);
@@ -881,7 +872,7 @@ int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex)
if ( n > 0 )
{
bytes[0] = unhex(hex[0]);
printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex));
LogPrintf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex));
}
bytes++;
hex++;
@@ -918,10 +909,8 @@ int32_t init_hexbytes_noT(char *hexbytes,unsigned char *message,long len)
{
hexbytes[i*2] = hexbyte((message[i]>>4) & 0xf);
hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf);
//printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]);
}
hexbytes[len*2] = 0;
//printf("len.%ld\n",len*2+1);
return((int32_t)len*2+1);
}
@@ -1087,7 +1076,7 @@ char *clonestr(char *str)
char *clone;
if ( str == 0 || str[0] == 0 )
{
printf("warning cloning nullstr.%p\n",str);
LogPrintf("warning cloning nullstr.%p\n",str);
#ifdef __APPLE__
while ( 1 ) sleep(1);
#endif
@@ -1109,7 +1098,7 @@ int32_t safecopy(char *dest,char *src,long len)
dest[i] = src[i];
if ( i == len )
{
printf("safecopy: %s too long %ld\n",src,len);
LogPrintf("safecopy: %s too long %ld\n",src,len);
#ifdef __APPLE__
//getchar();
#endif
@@ -1131,7 +1120,6 @@ char *parse_conf_line(char *line,char *field)
line++;
while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' )
line[strlen(line)-1] = 0;
//printf("LINE.(%s)\n",line);
_stripwhite(line,0);
return(clonestr(line));
}
@@ -1141,7 +1129,6 @@ double OS_milliseconds()
struct timeval tv; double millis;
gettimeofday(&tv,NULL);
millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.);
//printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis);
return(millis);
}
@@ -1193,7 +1180,7 @@ void queue_enqueue(char *name,queue_t *queue,struct queueitem *item)
strcpy(queue->name,name);
if ( item == 0 )
{
printf("FATAL type error: queueing empty value\n");
LogPrintf("FATAL type error: queueing empty value\n");
return;
}
lock_queue(queue);
@@ -1230,7 +1217,7 @@ void *queue_delete(queue_t *queue,struct queueitem *copy,int32_t copysize)
{
DL_DELETE(queue->list,item);
portable_mutex_unlock(&queue->mutex);
printf("name.(%s) deleted item.%p list.%p\n",queue->name,item,queue->list);
LogPrintf("name.(%s) deleted item.%p list.%p\n",queue->name,item,queue->list);
return(item);
}
}
@@ -1250,7 +1237,6 @@ void *queue_free(queue_t *queue)
DL_DELETE(queue->list,item);
free(item);
}
//printf("name.(%s) dequeue.%p list.%p\n",queue->name,item,queue->list);
}
portable_mutex_unlock(&queue->mutex);
return(0);
@@ -1268,7 +1254,6 @@ void *queue_clone(queue_t *clone,queue_t *queue,int32_t size)
memcpy(ptr,item,size);
queue_enqueue(queue->name,clone,ptr);
}
//printf("name.(%s) dequeue.%p list.%p\n",queue->name,item,queue->list);
}
portable_mutex_unlock(&queue->mutex);
return(0);
@@ -1304,7 +1289,6 @@ uint16_t _hush_userpass(char *username,char *password,FILE *fp)
{
if ( line[0] == '#' )
continue;
//printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword"));
if ( (str= strstr(line,(char *)"rpcuser")) != 0 )
rpcuser = parse_conf_line(str,(char *)"rpcuser");
else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 )
@@ -1312,7 +1296,6 @@ uint16_t _hush_userpass(char *username,char *password,FILE *fp)
else if ( (str= strstr(line,(char *)"rpcport")) != 0 )
{
port = atoi(parse_conf_line(str,(char *)"rpcport"));
//fprintf(stderr,"rpcport.%u in file\n",port);
}
}
if ( rpcuser != 0 && rpcpassword != 0 )
@@ -1320,7 +1303,6 @@ uint16_t _hush_userpass(char *username,char *password,FILE *fp)
strcpy(username,rpcuser);
strcpy(password,rpcpassword);
}
//printf("rpcuser.(%s) rpcpassword.(%s) HUSHUSERPASS.(%s) %u\n",rpcuser,rpcpassword,HUSHUSERPASS,port);
if ( rpcuser != 0 )
free(rpcuser);
if ( rpcpassword != 0 )
@@ -1340,7 +1322,7 @@ void hush_statefname(char *fname,char *symbol,char *str)
else
{
if ( strcmp(symbol,"ZZZ") != 0 )
printf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,SMART_CHAIN_SYMBOL,n,len,&fname[len - n]);
LogPrintf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,SMART_CHAIN_SYMBOL,n,len,&fname[len - n]);
return;
}
} else {
@@ -1353,7 +1335,6 @@ void hush_statefname(char *fname,char *symbol,char *str)
if ( symbol != 0 && symbol[0] != 0)
{
strcat(fname,symbol);
//printf("statefname.(%s) -> (%s)\n",symbol,fname);
#ifdef _WIN32
strcat(fname,"\\");
#else
@@ -1361,7 +1342,6 @@ void hush_statefname(char *fname,char *symbol,char *str)
#endif
}
strcat(fname,str);
//printf("test.(%s) -> [%s] statename.(%s) %s\n",test,SMART_CHAIN_SYMBOL,symbol,fname);
}
void hush_configfile(char *symbol,uint16_t rpcport)
@@ -1398,14 +1378,19 @@ void hush_configfile(char *symbol,uint16_t rpcport)
{
fprintf(fp,"rpcuser=user%u\nrpcpassword=pass%s\nrpcport=%u\nserver=1\ntxindex=1\nrpcworkqueue=4096\nrpcallowip=127.0.0.1\nrpcbind=127.0.0.1\n",crc,password,rpcport);
fclose(fp);
printf("Created (%s)\n",fname);
} else printf("Couldnt create (%s)\n",fname);
LogPrintf("Created (%s)\n",fname);
} else LogPrintf("Couldnt create (%s)\n",fname);
#endif
} else {
_hush_userpass(myusername,mypassword,fp);
mapArgs["-rpcpassword"] = mypassword;
mapArgs["-rpcusername"] = myusername;
//fprintf(stderr,"myusername.(%s)\n",myusername);
// Feed the credentials read by InitRPCAuthentication (httprpc.cpp) and the
// CLI (bitcoin-cli.cpp) -- both read "-rpcuser"/"-rpcpassword". Use SoftSetArg
// so a value passed on the command line (or an explicit -rpcuser/-rpcpassword)
// still wins: the old direct assignment silently overwrote a command-line
// -rpcpassword on every restart once this conf existed, and the username was
// written to a misspelled "-rpcusername" key that nothing ever reads.
SoftSetArg("-rpcpassword", mypassword);
SoftSetArg("-rpcuser", myusername);
fclose(fp);
}
}
@@ -1429,9 +1414,8 @@ void hush_configfile(char *symbol,uint16_t rpcport)
DRAGONX_PORT = hushport;
sprintf(HUSHUSERPASS,"%s:%s",username,password);
fclose(fp);
//printf("HUSH.(%s) -> userpass.(%s)\n",fname,HUSHUSERPASS);
} else {
printf("could not open.(%s)\n",fname);
LogPrintf("could not open.(%s)\n",fname);
}
}
@@ -1466,13 +1450,13 @@ uint32_t hush_smartmagic(char *symbol,uint64_t supply,uint8_t *extraptr,int32_t
{
vcalc_sha256(0,hash.bytes,extraptr,extralen);
crc0 = hash.uints[0];
fprintf(stderr,"DragonX raw magic=");
int32_t i; for (i=0; i<extralen; i++)
fprintf(stderr,"%02x",extraptr[i]);
fprintf(stderr," extralen=%d crc0=%x\n",extralen,crc0);
LogPrintf("DragonX raw magic extralen=%d crc0=%x\n",extralen,crc0);
}
//TODO: why is this needed?
// Legacy special case: HUSH3 mainnet had a hardcoded network magic (HUSH_MAGIC)
// rather than the crc32-derived value used by every other chain. This branch is
// dead on DragonX (SMART_CHAIN_SYMBOL is "DRAGONX", never "HUSH3"); it is kept only
// so the function still reproduces HUSH3's magic if ever run with that symbol.
const bool ishush3 = strncmp(symbol, "HUSH3",5) == 0 ? true : false;
if(ishush3) {
return HUSH_MAGIC;
@@ -1497,8 +1481,7 @@ uint16_t hush_port(char *symbol,uint64_t supply,uint32_t *magicp,uint8_t *extrap
fprintf(stderr,"%s: extralen=%d\n",__func__,extralen);
*magicp = hush_smartmagic(symbol,supply,extraptr,extralen);
//if(fDebug)
fprintf(stderr,"%s: extralen=%d, supply=%lu\n",__func__,extralen, supply);
LogPrintf("%s: extralen=%d, supply=%lu\n",__func__,extralen, supply);
return(hush_smartport(*magicp,extralen));
}
@@ -1519,16 +1502,19 @@ uint64_t hush_max_money()
return hush_current_supply(10000000);
}
// This implements the Hush Emission Curve, the miner subsidy part,
// and must be kept in sync with hush_commision() in hush_bitcoind.h!
// Changing these functions are consensus changes!
// Here Be Dragons! -- Duke Leto
// This implements the emission curve (miner subsidy part) and must be kept in
// sync with hush_commission() in hush_bitcoind.h! Changing these functions,
// including the height literals below, is a CONSENSUS change.
// NOTE: this TRANSITION boundary is 128 here, while hush_commission() uses 129.
// This off-by-one between the two curves is a historical consensus quirk and is
// deliberately left as-is: changing either value would be a consensus change.
uint64_t hush_block_subsidy(int height)
{
uint64_t subsidy = 0;
int32_t HALVING1 = GetArg("-z2zheight",340000);
//TODO: support INTERVAL :(
//int32_t INTERVAL = GetArg("-ac_halving1",840000);
// Consensus: TRANSITION is 128 here vs 129 in hush_commission(); do not change (see note above).
int32_t TRANSITION = 128;
if (height < TRANSITION) {
@@ -1564,14 +1550,14 @@ uint64_t hush_block_subsidy(int height)
subsidy = 549316;
} else if (height < 23860000) {
subsidy = 274658;
} else if (height < 23860000) {
subsidy = 137329;
// removed unreachable duplicate `height < 23860000` (=> 137329); kept in sync
// with hush_commission() — the schedule drops straight to 68664 next.
} else if (height < 25540000) {
subsidy = 68664;
} else if (height < 27220000) {
subsidy = 34332;
} else if (height < 27220000) {
subsidy = 17166;
// removed unreachable duplicate `height < 27220000` (=> 17166); kept in sync
// with hush_commission() — the schedule drops straight to 8583 next.
} else if (height < 28900000) {
subsidy = 8583;
} else if (height < 30580000) {
@@ -1611,7 +1597,9 @@ uint64_t hush_block_subsidy(int height)
return subsidy;
}
// wrapper for more general supply curves of Hush Arrakis Chains
// Wrapper for the more general supply curves used by assetchains (era/halving/decay driven).
// On DragonX the reward comes from the -ac_reward/-ac_halving parameters set in hush_args();
// the ishush3 branch below is a legacy special case that is dead on DragonX.
uint64_t hush_sc_block_subsidy(int nHeight)
{
// Find current era, start from beginning reward, and determine current subsidy
@@ -1619,12 +1607,13 @@ uint64_t hush_sc_block_subsidy(int nHeight)
int64_t subsidyDifference;
int32_t numhalvings = 0, curEra = 0, sign = 1;
static uint64_t cached_subsidy; static int32_t cached_numhalvings; static int cached_era;
// Legacy-HUSH3 detection: dead on DragonX (SMART_CHAIN_SYMBOL is "DRAGONX"), used only
// to route HUSH3 mainnet through its bespoke hush_block_subsidy() emission curve below.
const bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
// fprintf(stderr,"%s: ht=%d ishush3=%d\n", __func__, nHeight, ishush3);
// check for backwards compat, older chains with no explicit rewards had 0.0001 block reward
if ( ASSETCHAINS_ENDSUBSIDY[0] == 0 && ASSETCHAINS_REWARD[0] == 0 ) {
fprintf(stderr,"%s: defaulting to 0.0001 subsidy\n",__func__);
LogPrintf("%s: defaulting to 0.0001 subsidy\n",__func__);
subsidy = 10000;
} else if ( (ASSETCHAINS_ENDSUBSIDY[0] == 0 && ASSETCHAINS_REWARD[0] != 0) || ASSETCHAINS_ENDSUBSIDY[0] != 0 ) {
// if we have an end block in the first era, find our current era
@@ -1656,10 +1645,11 @@ uint64_t hush_sc_block_subsidy(int nHeight)
if(fDebug)
fprintf(stderr,"%s: HUSH3 subsidy=%ld at height=%d\n",__func__,subsidy,nHeight);
} else if ( (numhalvings = ((nHeight - nStart) / ASSETCHAINS_HALVING[curEra])) > 0 ) {
// The code below is not compatible with HUSH3 mainnet
// Generic halving/decay path used by DragonX and other assetchains.
// (Legacy HUSH3 mainnet did NOT use this path; it took the ishush3
// branch above, which reproduces its bespoke emission curve.)
if ( ASSETCHAINS_DECAY[curEra] == 0 ) {
subsidy >>= numhalvings;
// fprintf(stderr,"%s: no decay, numhalvings.%d curEra.%d subsidy.%ld nStart.%ld\n",__func__, numhalvings, curEra, subsidy, nStart);
} else if ( ASSETCHAINS_DECAY[curEra] == 100000000 && ASSETCHAINS_ENDSUBSIDY[curEra] != 0 ) {
if ( curEra == ASSETCHAINS_LASTERA )
{
@@ -1675,12 +1665,11 @@ uint64_t hush_sc_block_subsidy(int nHeight)
}
denominator = ASSETCHAINS_ENDSUBSIDY[curEra] - nStart;
numerator = denominator - ((ASSETCHAINS_ENDSUBSIDY[curEra] - nHeight) + ((nHeight - nStart) % ASSETCHAINS_HALVING[curEra]));
// fprintf(stderr,"%s: numerator=%ld , denominator=%ld at height=%d\n",__func__,numerator, denominator,nHeight);
if( denominator ) {
subsidy = subsidy - sign * ((subsidyDifference * numerator) / denominator);
} else {
fprintf(stderr,"%s: invalid denominator=%ld !\n", __func__, denominator);
fprintf(stderr,"%s: defaulting to 0.0001 subsidy\n",__func__);
LogPrintf("%s: invalid denominator=%ld !\n", __func__, denominator);
LogPrintf("%s: defaulting to 0.0001 subsidy\n",__func__);
subsidy = 10000;
}
} else {
@@ -1698,13 +1687,13 @@ uint64_t hush_sc_block_subsidy(int nHeight)
}
}
} else {
fprintf(stderr,"%s: curEra.%d > lastEra.%lu\n", __func__, curEra, ASSETCHAINS_LASTERA);
LogPrintf("%s: curEra.%d > lastEra.%lu\n", __func__, curEra, ASSETCHAINS_LASTERA);
}
}
uint32_t magicExtra = ASSETCHAINS_STAKED ? ASSETCHAINS_MAGIC : (ASSETCHAINS_MAGIC & 0xffffff);
if ( ASSETCHAINS_SUPPLY > 10000000000 ) // over 10 billion?
{
fprintf(stderr,"%s: Detected supply over 10 billion, danger zone!\n",__func__);
LogPrintf("%s: Detected supply over 10 billion, danger zone!\n",__func__);
if ( nHeight <= ASSETCHAINS_SUPPLY/1000000000 )
{
subsidy += (uint64_t)1000000000 * COIN;
@@ -1782,7 +1771,7 @@ void hush_args(char *argv0)
IS_HUSH_NOTARY = 1;
HUSH_MININGTHREADS = 1;
mapArgs ["-genproclimit"] = itostr(HUSH_MININGTHREADS);
fprintf(stderr,"running as notary.%d %s\n",i,notaries_list[hush_season-1][i][0]);
LogPrintf("running as notary.%d %s\n",i,notaries_list[hush_season-1][i][0]);
break;
}
}
@@ -1799,14 +1788,23 @@ void hush_args(char *argv0)
LogPrint("net", "%s: isdragonx=%d\n", __func__, isdragonx);
if (isdragonx) {
// node8-node10 are PLACEHOLDERS with no DNS records yet. A hostname that
// does not resolve is harmless here: ThreadOpenAddedConnections just fails
// to open the connection and retries on its 2-minute cycle. Reserving the
// names in the binary means a future seed can be brought into the -addnode
// set by creating one DNS record, with no release and no waiting for users
// to upgrade. (seed.dragonx.is already provides that for the DNS-seed path;
// this extends the same property to the addnode path.)
DRAGONX_nodes = {"node1.dragonx.is","node2.dragonx.is","node3.dragonx.is",
"node4.dragonx.is","node5.dragonx.is"
"node4.dragonx.is","node5.dragonx.is","node6.dragonx.is",
"node7.dragonx.is","node8.dragonx.is","node9.dragonx.is",
"node10.dragonx.is"
};
}
vector<string> more_nodes = mapMultiArgs["-addnode"];
if (more_nodes.size() > 0) {
fprintf(stderr,"%s: Adding %lu more nodes via custom -addnode arguments\n", __func__, more_nodes.size() );
LogPrint("net", "%s: Adding %lu more nodes via custom -addnode arguments\n", __func__, more_nodes.size() );
}
// Add default DRAGONX nodes after custom addnodes, if applicable
if(DRAGONX_nodes.size() > 0) {
@@ -1848,19 +1846,19 @@ void hush_args(char *argv0)
if ( i > 1 && ccEnablesHeight[i-2] == ecode )
break;
if ( ecode > 255 || ecode < 0 )
fprintf(stderr, "ac_ccactivateht: invalid evalcode.%i must be between 0 and 256.\n", ecode);
LogPrintf("ac_ccactivateht: invalid evalcode.%i must be between 0 and 256.\n", ecode);
else if ( ht > 0 )
{
// update global map.
mapHeightEvalActivate[ecode] = ht;
fprintf(stderr, "ac_ccactivateht: ecode.%i activates at height.%i\n", ecode, mapHeightEvalActivate[ecode]);
LogPrintf("ac_ccactivateht: ecode.%i activates at height.%i\n", ecode, mapHeightEvalActivate[ecode]);
}
i++;
}
if ( (HUSH_REWIND= GetArg("-rewind",0)) != 0 )
{
printf("HUSH_REWIND %d\n",HUSH_REWIND);
LogPrintf("HUSH_REWIND %d\n",HUSH_REWIND);
}
HUSH_EARLYTXID = Parseuint256(GetArg("-earlytxid","0").c_str());
ASSETCHAINS_EARLYTXIDCONTRACT = GetArg("-ac_earlytxidcontract",0);
@@ -1878,7 +1876,7 @@ void hush_args(char *argv0)
STAKING_MIN_DIFF = ASSETCHAINS_MINDIFF[i];
// only worth mentioning if it's not equihash
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH)
printf("ASSETCHAINS_ALGO, algorithm set to %s\n", selectedAlgo.c_str());
LogPrintf("ASSETCHAINS_ALGO, algorithm set to %s\n", selectedAlgo.c_str());
break;
}
}
@@ -1888,11 +1886,11 @@ void hush_args(char *argv0)
{
printf("equihash values N.%li and K.%li are not currently available\n", ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]);
exit(0);
} else printf("ASSETCHAINS_ALGO, algorithm set to equihash with N.%li and K.%li\n", ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]);
} else LogPrintf("ASSETCHAINS_ALGO, algorithm set to equihash with N.%li and K.%li\n", ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]);
}
if (i == ASSETCHAINS_NUMALGOS)
{
printf("ASSETCHAINS_ALGO, %s not supported. using equihash\n", selectedAlgo.c_str());
LogPrintf("ASSETCHAINS_ALGO, %s not supported. using equihash\n", selectedAlgo.c_str());
}
// Set our symbol from -ac_name value
@@ -1907,14 +1905,14 @@ void hush_args(char *argv0)
} else {
ASSETCHAINS_RANDOMX_VALIDATION = 1; // all other RandomX HACs: enforce from height 1
}
printf("ASSETCHAINS_RANDOMX_VALIDATION set to %d for %s\n", ASSETCHAINS_RANDOMX_VALIDATION, SMART_CHAIN_SYMBOL);
LogPrintf("ASSETCHAINS_RANDOMX_VALIDATION set to %d for %s\n", ASSETCHAINS_RANDOMX_VALIDATION, SMART_CHAIN_SYMBOL);
}
ASSETCHAINS_LASTERA = GetArg("-ac_eras", 1);
if ( ASSETCHAINS_LASTERA < 1 || ASSETCHAINS_LASTERA > ASSETCHAINS_MAX_ERAS )
{
ASSETCHAINS_LASTERA = 1;
printf("ASSETCHAINS_LASTERA, if specified, must be between 1 and %u. ASSETCHAINS_LASTERA set to %lu\n", ASSETCHAINS_MAX_ERAS, ASSETCHAINS_LASTERA);
LogPrintf("ASSETCHAINS_LASTERA, if specified, must be between 1 and %u. ASSETCHAINS_LASTERA set to %lu\n", ASSETCHAINS_MAX_ERAS, ASSETCHAINS_LASTERA);
}
ASSETCHAINS_LASTERA -= 1;
if(fDebug)
@@ -1925,7 +1923,7 @@ void hush_args(char *argv0)
ASSETCHAINS_TIMEUNLOCKTO = GetArg("-ac_timeunlockto", 0);
if ( ASSETCHAINS_TIMEUNLOCKFROM > ASSETCHAINS_TIMEUNLOCKTO )
{
printf("ASSETCHAINS_TIMELOCKGTE - must specify valid ac_timeunlockfrom and ac_timeunlockto\n");
LogPrintf("ASSETCHAINS_TIMELOCKGTE - must specify valid ac_timeunlockfrom and ac_timeunlockto\n");
ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
ASSETCHAINS_TIMEUNLOCKFROM = ASSETCHAINS_TIMEUNLOCKTO = 0;
}
@@ -1944,7 +1942,7 @@ void hush_args(char *argv0)
ASSETCHAINS_SCRIPTPUB = GetArg("-ac_script","");
fprintf(stderr,"%s: Setting custom %s reward isdragonx=%d reward,halving,subsidy chain values...\n",__func__, SMART_CHAIN_SYMBOL, isdragonx);
LogPrintf("%s: Setting custom %s reward isdragonx=%d reward,halving,subsidy chain values...\n",__func__, SMART_CHAIN_SYMBOL, isdragonx);
if(isdragonx) {
// DragonX chain parameters (previously set via wrapper script)
// -ac_name=DRAGONX -ac_algo=randomx -ac_halving=3500000 -ac_reward=300000000 -ac_blocktime=36 -ac_private=1
@@ -1960,12 +1958,12 @@ void hush_args(char *argv0)
if ( ASSETCHAINS_DECAY[i] == 100000000 && ASSETCHAINS_ENDSUBSIDY == 0 )
{
ASSETCHAINS_DECAY[i] = 0;
printf("ERA%u: ASSETCHAINS_DECAY of 100000000 means linear and that needs ASSETCHAINS_ENDSUBSIDY\n", i);
LogPrintf("ERA%u: ASSETCHAINS_DECAY of 100000000 means linear and that needs ASSETCHAINS_ENDSUBSIDY\n", i);
}
else if ( ASSETCHAINS_DECAY[i] > 100000000 )
{
ASSETCHAINS_DECAY[i] = 0;
printf("ERA%u: ASSETCHAINS_DECAY cant be more than 100000000\n", i);
LogPrintf("ERA%u: ASSETCHAINS_DECAY cant be more than 100000000\n", i);
}
}
@@ -1991,21 +1989,15 @@ void hush_args(char *argv0)
SplitStr(GetArg("-ac_stocks",""), ASSETCHAINS_STOCKS);
if ( ASSETCHAINS_STOCKS.size() > 0 )
ASSETCHAINS_CBOPRET |= 8;
for (i=0; i<ASSETCHAINS_PRICES.size(); i++)
fprintf(stderr,"%s ",ASSETCHAINS_PRICES[i].c_str());
fprintf(stderr,"%d -ac_prices\n",(int32_t)ASSETCHAINS_PRICES.size());
for (i=0; i<ASSETCHAINS_STOCKS.size(); i++)
fprintf(stderr,"%s ",ASSETCHAINS_STOCKS[i].c_str());
fprintf(stderr,"%d -ac_stocks\n",(int32_t)ASSETCHAINS_STOCKS.size());
LogPrintf("%d -ac_prices\n",(int32_t)ASSETCHAINS_PRICES.size());
LogPrintf("%d -ac_stocks\n",(int32_t)ASSETCHAINS_STOCKS.size());
}
hexstr = GetArg("-ac_mineropret","");
if ( hexstr.size() != 0 )
{
Mineropret.resize(hexstr.size()/2);
decode_hex(Mineropret.data(),hexstr.size()/2,(char *)hexstr.c_str());
for (i=0; i<Mineropret.size(); i++)
fprintf(stderr,"%02x",Mineropret[i]);
fprintf(stderr," Mineropret\n");
LogPrintf(" Mineropret\n");
}
if ( ASSETCHAINS_COMMISSION != 0 && ASSETCHAINS_FOUNDERS_REWARD != 0 )
{
@@ -2017,7 +2009,8 @@ void hush_args(char *argv0)
uint8_t prevCCi = 0;
ASSETCHAINS_CCLIB = GetArg("-ac_cclib","hush3");
// these are the enabled CCs on HUSH3 mainnet
// Default CC set inherited from legacy HUSH3 mainnet; only used when a chain
// enables CryptoConditions and does not override -ac_ccenable.
Split(GetArg("-ac_ccenable","228,234,235,236,241"), sizeof(ccenables)/sizeof(*ccenables), ccenables, 0);
for (i=nonz=0; i<0x100; i++)
{
@@ -2025,10 +2018,9 @@ void hush_args(char *argv0)
{
nonz++;
prevCCi = ccenables[i];
fprintf(stderr,"%d ",(uint8_t)(ccenables[i] & 0xff));
}
}
fprintf(stderr,"nonz.%d ccenables[]\n",nonz);
LogPrintf("nonz.%d ccenables[]\n",nonz);
if ( nonz > 0 )
{
for (i=0; i<256; i++)
@@ -2128,9 +2120,9 @@ void hush_args(char *argv0)
if ( ASSETCHAINS_FOUNDERS_REWARD == 0 )
{
ASSETCHAINS_COMMISSION = 53846154; // maps to 35%
printf("ASSETCHAINS_COMMISSION defaulted to 35%% when founders reward active\n");
LogPrintf("ASSETCHAINS_COMMISSION defaulted to 35%% when founders reward active\n");
} else {
printf("ASSETCHAINS_FOUNDERS_REWARD set to %ld\n", ASSETCHAINS_FOUNDERS_REWARD);
LogPrintf("ASSETCHAINS_FOUNDERS_REWARD set to %ld\n", ASSETCHAINS_FOUNDERS_REWARD);
}
/*else if ( ASSETCHAINS_SELFIMPORT.size() == 0 )
{
@@ -2142,12 +2134,12 @@ void hush_args(char *argv0)
if ( ASSETCHAINS_COMMISSION != 0 )
{
ASSETCHAINS_COMMISSION = 0;
printf("ASSETCHAINS_COMMISSION needs an ASSETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n");
LogPrintf("ASSETCHAINS_COMMISSION needs an ASSETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n");
}
if ( ASSETCHAINS_FOUNDERS != 0 )
{
ASSETCHAINS_FOUNDERS = 0;
printf("ASSETCHAINS_FOUNDERS needs an ASSETCHAINS_OVERRIDE_PUBKEY or ASSETCHAINS_SCRIPTPUB\n");
LogPrintf("ASSETCHAINS_FOUNDERS needs an ASSETCHAINS_OVERRIDE_PUBKEY or ASSETCHAINS_SCRIPTPUB\n");
}
}
@@ -2215,7 +2207,7 @@ void hush_args(char *argv0)
// NOTE: Hush does not use this, we use -ac_script to implement our FR -- Duke
if ( ASSETCHAINS_FOUNDERS_REWARD != 0 )
{
fprintf(stderr, "set founders reward.%lld\n",(long long)ASSETCHAINS_FOUNDERS_REWARD);
LogPrintf("set founders reward.%lld\n",(long long)ASSETCHAINS_FOUNDERS_REWARD);
extralen += dragon_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS_REWARD),(void *)&ASSETCHAINS_FOUNDERS_REWARD);
}
}
@@ -2224,14 +2216,12 @@ void hush_args(char *argv0)
decode_hex(&extraptr[extralen],ASSETCHAINS_SCRIPTPUB.size()/2,(char *)ASSETCHAINS_SCRIPTPUB.c_str());
extralen += ASSETCHAINS_SCRIPTPUB.size()/2;
//extralen += dragon_rwnum(1,&extraptr[extralen],(int32_t)ASSETCHAINS_SCRIPTPUB.size(),(void *)ASSETCHAINS_SCRIPTPUB.c_str());
fprintf(stderr,"append ac_script %s\n",ASSETCHAINS_SCRIPTPUB.c_str());
LogPrintf("append ac_script %s\n",ASSETCHAINS_SCRIPTPUB.c_str());
}
if ( ASSETCHAINS_SELFIMPORT.size() > 0 )
{
memcpy(&extraptr[extralen],(char *)ASSETCHAINS_SELFIMPORT.c_str(),ASSETCHAINS_SELFIMPORT.size());
for (i=0; i<ASSETCHAINS_SELFIMPORT.size(); i++)
fprintf(stderr,"%c",extraptr[extralen+i]);
fprintf(stderr," selfimport\n");
LogPrintf(" selfimport\n");
extralen += ASSETCHAINS_SELFIMPORT.size();
}
if ( ASSETCHAINS_BEAMPORT != 0 )
@@ -2241,7 +2231,7 @@ void hush_args(char *argv0)
if ( ASSETCHAINS_MARMARA != 0 )
extraptr[extralen++] = ASSETCHAINS_MARMARA;
fprintf(stderr,"extralen.%d before disable bits\n",extralen);
LogPrintf("extralen.%d before disable bits\n",extralen);
if ( nonz > 0 ) {
memcpy(&extraptr[extralen],disablebits,sizeof(disablebits));
@@ -2252,14 +2242,13 @@ void hush_args(char *argv0)
for (i=0; i<ASSETCHAINS_CCLIB.size(); i++)
{
extraptr[extralen++] = ASSETCHAINS_CCLIB[i];
fprintf(stderr,"%c",ASSETCHAINS_CCLIB[i]);
}
fprintf(stderr," <- CCLIB name\n");
LogPrintf(" <- CCLIB name\n");
}
if ( ASSETCHAINS_BLOCKTIME != 60 ) {
extralen += dragon_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_BLOCKTIME),(void *)&ASSETCHAINS_BLOCKTIME);
fprintf(stderr,"%s: ASSETCHAINS_BLOCKTIME=%d, extralen=%d\n", __func__, ASSETCHAINS_BLOCKTIME, extralen);
LogPrintf("%s: ASSETCHAINS_BLOCKTIME=%d, extralen=%d\n", __func__, ASSETCHAINS_BLOCKTIME, extralen);
}
if ( Mineropret.size() != 0 )
@@ -2290,7 +2279,7 @@ void hush_args(char *argv0)
}
//hush_pricesinit();
hush_cbopretupdate(1); // will set Mineropret
fprintf(stderr,"This blockchain uses data produced from CoinDesk Bitcoin Price Index\n");
LogPrintf("This blockchain uses data produced from CoinDesk Bitcoin Price Index\n");
}
if ( ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0 )
{
@@ -2346,13 +2335,12 @@ void hush_args(char *argv0)
MAX_MONEY = HUSH_MAXNVALUE;
if(fDebug)
fprintf(stderr,"MAX_MONEY %llu %.8f\n",(long long)MAX_MONEY,(double)MAX_MONEY/SATOSHIDEN);
//printf("baseid.%d MAX_MONEY.%s %.8f\n",baseid,SMART_CHAIN_SYMBOL,(double)MAX_MONEY/SATOSHIDEN);
uint16_t tmpport = hush_port(SMART_CHAIN_SYMBOL,ASSETCHAINS_SUPPLY,&ASSETCHAINS_MAGIC,extraptr,extralen);
if ( GetArg("-port",0) != 0 )
{
ASSETCHAINS_P2PPORT = GetArg("-port",0);
if(ishush3) {
fprintf(stderr,"set HUSH3 p2pport.%u\n",ASSETCHAINS_P2PPORT);
LogPrintf("set HUSH3 p2pport.%u\n",ASSETCHAINS_P2PPORT);
ASSETCHAINS_P2PPORT = 18030;
}
if(fDebug)
@@ -2368,7 +2356,6 @@ void hush_args(char *argv0)
boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
#endif
}
//fprintf(stderr,"Got datadir.(%s)\n",dirname);
if ( SMART_CHAIN_SYMBOL[0] != 0 )
{
int32_t hush_baseid(char *origbase);
@@ -2386,7 +2373,6 @@ void hush_args(char *argv0)
fprintf(stderr,"ac_cbmaturity must be >0, shutting down\n");
StartShutdown();
}
//fprintf(stderr,"ASSETCHAINS_RPCPORT (%s) %u\n",SMART_CHAIN_SYMBOL,ASSETCHAINS_RPCPORT);
}
if ( ASSETCHAINS_RPCPORT == 0 )
ASSETCHAINS_RPCPORT = ASSETCHAINS_P2PPORT + 1;
@@ -2402,13 +2388,16 @@ void hush_args(char *argv0)
if ( HUSH_CCACTIVATE != 0 )
{
ASSETCHAINS_CC = 2;
fprintf(stderr,"smart utxo CC contracts will activate at height.%d\n",HUSH_CCACTIVATE);
LogPrintf("smart utxo CC contracts will activate at height.%d\n",HUSH_CCACTIVATE);
} else if ( ccEnablesHeight[0] != 0 ) {
ASSETCHAINS_CC = 2;
fprintf(stderr,"smart utxo CC contract %d will activate at height.%d\n",(int32_t)ccEnablesHeight[0],(int32_t)ccEnablesHeight[1]);
LogPrintf("smart utxo CC contract %d will activate at height.%d\n",(int32_t)ccEnablesHeight[0],(int32_t)ccEnablesHeight[1]);
}
}
} else {
// Legacy fallback path taken only when no -ac_name is set (SMART_CHAIN_SYMBOL empty).
// Dead on DragonX, which always runs with -ac_name=DRAGONX. The HUSH3/Bitcoin conf
// paths and default ports below are historical and left as-is for backwards compat.
char fname[512],username[512],password[4096]; int32_t iter; FILE *fp;
ASSETCHAINS_P2PPORT = 7770;
ASSETCHAINS_RPCPORT = 7771;
@@ -2439,8 +2428,7 @@ void hush_args(char *argv0)
_hush_userpass(username,password,fp);
sprintf(iter == 0 ? HUSHUSERPASS : BTCUSERPASS,"%s:%s",username,password);
fclose(fp);
//printf("HUSH.(%s) -> userpass.(%s)\n",fname,HUSHUSERPASS);
} //else printf("couldnt open.(%s)\n",fname);
}
if ( IS_HUSH_NOTARY == 0 )
break;
}
@@ -2449,7 +2437,6 @@ void hush_args(char *argv0)
if ( SMART_CHAIN_SYMBOL[0] != 0 )
{
BITCOIND_RPCPORT = GetArg("-rpcport", ASSETCHAINS_RPCPORT);
//fprintf(stderr,"(%s) port.%u chain params initialized\n",SMART_CHAIN_SYMBOL,BITCOIND_RPCPORT);
// Set custom cc rulse for chains here
if ( strcmp("HUSH3",SMART_CHAIN_SYMBOL) == 0 ) {
@@ -2505,7 +2492,7 @@ void hush_prefetch(FILE *fp)
{
rewind(fp);
while ( fread(ignore,1,incr,fp) == incr ) // prefetch
fprintf(stderr,".");
;
free(ignore);
}
}

View File

@@ -60,6 +60,7 @@
#include "wallet/wallet.h"
#include "wallet/walletdb.h"
#include "wallet/asyncrpcoperation_saplingconsolidation.h"
#include "wallet/asyncrpcoperation_autoshieldcoinbase.h"
#include "wallet/asyncrpcoperation_sweep.h"
#endif
#include <stdint.h>
@@ -120,7 +121,11 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
static const char* DEFAULT_ASMAP_FILENAME="asmap.dat";
CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h
// LevelDB read-cache size (bytes) for the notarizations (dPoW) DB. Non-consensus: just the
// in-memory cache the DB is opened with; the value here does not affect validation.
static const size_t NOTARIZATION_DB_CACHE_BYTES = 100 * 1024 * 1024; // 100 MiB
CClientUIInterface uiInterface; // global UI callback dispatcher (declared extern in ui_interface.h)
// Shutdown
//
@@ -148,7 +153,7 @@ std::atomic<bool> fRequestShutdown(false);
void StartShutdown()
{
if(fDebug) {
fprintf(stderr,"%s: fRequestShudown=true\n", __FUNCTION__);
fprintf(stderr,"%s: fRequestShutdown=true\n", __FUNCTION__);
}
fRequestShutdown = true;
}
@@ -490,9 +495,11 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-zsweepexclude", _("Addresses to exclude from sweeping (default none)"));
strUsage += HelpMessageOpt("-autoshield", _("Automatically shield matured coinbase (mining rewards) into a seed-derived wallet z-address (default: true for wallets created or restored by this software, false when the HD seed provenance is unknown). No-op when not mining or wallet is locked."));
strUsage += HelpMessageOpt("-autoshieldinterval", strprintf(_("Block interval between automatic coinbase-shielding rounds (default: %i, min 5)"), 25));
strUsage += HelpMessageOpt("-autoshieldinterval", strprintf(_("Block interval between automatic coinbase-shielding rounds (default: %i, min %i)"), DEFAULT_AUTOSHIELD_INTERVAL, MIN_AUTOSHIELD_INTERVAL));
strUsage += HelpMessageOpt("-autoshieldaddress=<zaddr>", _("Destination Sapling z-address for auto-shielded coinbase (default: reuse or create a wallet z-address). Must be spendable by this wallet."));
strUsage += HelpMessageOpt("-autoshieldfee", strprintf(_("Fee in puposhis for automatic coinbase-shielding transactions (default: %i)"), 10000));
strUsage += HelpMessageOpt("-autoshieldfee", strprintf(_("Fee in puposhis for automatic coinbase-shielding transactions (default: %i)"), DEFAULT_AUTOSHIELD_FEE));
strUsage += HelpMessageOpt("-sietch-min-zouts=<n>", strprintf(_("Minimum number of shielded (Sapling) outputs Sietch adds to each z_sendmany transaction as decoys, strengthening amount/linkability privacy. Higher values add privacy at the cost of larger transactions (default: %u, clamped to the range 3-50)"), 7));
strUsage += HelpMessageOpt("-autoshieldminutxos", strprintf(_("Only auto-shield once at least this many matured coinbase UTXOs exist (default: %i)"), 1));
strUsage += HelpMessageOpt("-deletetx", _("Enable Old Transaction Deletion"));
@@ -614,12 +621,13 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("Stratum server options:"));
strUsage += HelpMessageOpt("-stratum", _("Enable stratum server (default: off)"));
strUsage += HelpMessageOpt("-stratumtarget=<hex>", _("Pool share target (64-hex, big-endian; larger = easier). Default is the diff-1 target. Useful for solo/low-difficulty mining."));
strUsage += HelpMessageOpt("-stratumaddress=<address>", _("Mining address to use when special address of 'x' is sent by miner (default: none)"));
strUsage += HelpMessageOpt("-stratumbind=<ipaddr>", _("Bind to given address to listen for Stratum work requests. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)"));
strUsage += HelpMessageOpt("-stratumport=<port>", strprintf(_("Listen for Stratum work requests on <port> (default: %u or testnet: %u)"), BaseParams().StratumPort(), BaseParams().StratumPort()));
strUsage += HelpMessageOpt("-stratumallowip=<ip>", _("Allow Stratum work requests from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"));
// "ac" stands for "affects consensus" or Arrakis Chain
// "ac" prefix is inherited from the Komodo asset-chain lineage ("asset chain"/"affects consensus")
strUsage += HelpMessageGroup(_("DragonX Chain options:"));
strUsage += HelpMessageOpt("-ac_algo", _("Choose PoW mining algorithm, either 'equihash' or 'randomx'. default is Equihash (200,9)"));
strUsage += HelpMessageOpt("-ac_blocktime", _("Block time in seconds, default is 60"));
@@ -786,7 +794,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
}
/** Sanity checks
* Ensure that Hush is running in a usable environment with all
* Ensure that DragonX is running in a usable environment with all
* necessary library support.
*/
bool InitSanityCheck(void)
@@ -907,7 +915,7 @@ static void ZC_LoadParams(const CChainParams& chainparams)
if (!found) {
// The traditional place Zcash params are stored, should not hit this case in normal circumstances,
// as Hush packages sapling params now
// as DragonX packages sapling params now
sapling_spend = ZC_GetParamsDir() / "sapling-spend.params";
sapling_output = ZC_GetParamsDir() / "sapling-output.params";
if (files_exist(sapling_spend, sapling_output)) {
@@ -926,7 +934,7 @@ static void ZC_LoadParams(const CChainParams& chainparams)
boost::system::error_code ec1, ec2;
boost::uintmax_t spend_size = file_size(sapling_spend, ec1);
boost::uintmax_t output_size = file_size(sapling_output, ec2);
fprintf(stderr,"Sapling spend: %d bytes, output: %d bytes\n", (int)spend_size, (int)output_size);
LogPrintf("Sapling spend: %d bytes, output: %d bytes\n", (int)spend_size, (int)output_size);
// We could check sha hashes, but we mostly want to detect on-disk file corruption
// or people having a full harddrive. Full validation happens in librustzcash_init_zksnark_params
@@ -979,11 +987,15 @@ static void ZC_LoadParams(const CChainParams& chainparams)
bool AppInitServers(boost::thread_group& threadGroup)
{
fprintf(stderr,"%s: start\n",__func__);
LogPrintf("%s: start\n",__func__);
RPCServer::OnStopped(&OnRPCStopped);
RPCServer::OnPreCommand(&OnRPCPreCommand);
if (!InitHTTPServer())
return false;
// Stratum server (stratum.cpp) supports DragonX's RandomX PoW (32-byte solution + per-height
// RandomX key conveyed to the miner) as well as legacy Equihash, branched on ASSETCHAINS_ALGO.
// Off by default (DEFAULT_STRATUM_ENABLE=false); only -stratum turns it on. Needs a RandomX-aware
// stratum miner (see contrib/ reference miner) — stock Equihash/Monero miners won't work.
if (GetBoolArg("-stratum", DEFAULT_STRATUM_ENABLE) && !InitStratumServer())
return false;
if (!StartRPC())
@@ -997,7 +1009,7 @@ bool AppInitServers(boost::thread_group& threadGroup)
return true;
}
/** Initialize Hush.
/** Initialize DragonX.
* @pre Parameters should be parsed and config file should be read.
*/
extern int32_t HUSH_REWIND;
@@ -1121,7 +1133,6 @@ static void AdjustCoinCacheForMemoryPressure()
bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
{
//fprintf(stderr,"%s start\n", __FUNCTION__);
// ********************************************************* Step 1: setup
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
@@ -1158,7 +1169,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError("Error: -sysperms is not allowed in combination with enabled wallet functionality");
#endif
} else {
//fprintf(stderr,"%s setting umask\n", __FUNCTION__);
umask(077);
}
@@ -1176,12 +1186,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
std::set_new_handler(new_handler_terminate);
//fprintf(stderr,"%s: set signal handlers\n", __FUNCTION__);
// ********************************************************* Step 2: parameter interactions
const CChainParams& chainparams = Params();
//fprintf(stderr,"%s: got chain params\n", __FUNCTION__);
// Set this early so that experimental features are correctly enabled/disabled
fExperimentalMode = GetBoolArg("-experimentalfeatures", true);
@@ -1192,11 +1200,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Fail early if user has set experimental options without the global flag
if (!fExperimentalMode) {
if (mapArgs.count("-developerencryptwallet")) {
fprintf(stderr,"%s wallet encryption error\n", __FUNCTION__);
LogPrintf("%s wallet encryption error\n", __FUNCTION__);
return InitError(_("Wallet encryption requires -experimentalfeatures."));
}
}
//fprintf(stderr,"%s tik2\n", __FUNCTION__);
// Set this early so that parameter interactions go to console
fPrintToConsole = GetBoolArg("-printtoconsole", false);
@@ -1205,7 +1212,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("Hush version %s (%s)\n", FormatFullVersion());
LogPrintf("DragonX version %s (%s)\n", FormatFullVersion());
#ifdef DEBUG_LOCKORDER
@@ -1225,7 +1232,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("%s: parameter interaction: -allowbind set -> setting -listen=1\n", __func__);
}
//fprintf(stderr,"%s tik3\n", __FUNCTION__);
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
// when only connecting to trusted nodes, do not seed via DNS, or listen by default
if (SoftSetBoolArg("-dnsseed", false))
@@ -1257,7 +1263,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
}
// Read asmap file by default for HUSH3 and all Hush Arrakis Chains
// Read asmap file by default on DragonX
if (GetArg("-asmap",1)) {
fs::path asmap_path = fs::path(GetArg("-asmap", ""));
@@ -1275,36 +1281,36 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (asmap_path.empty()) {
// Most binaries will have it in PWD
asmap_path = pwd / DEFAULT_ASMAP_FILENAME;
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
if(fs::exists(asmap_path)) {
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
} else {
// Debian Packages
asmap_path = fs::path("/usr/share/hush") / DEFAULT_ASMAP_FILENAME;
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
if(fs::exists(asmap_path)) {
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
} else {
// Source code
asmap_path = contrib / DEFAULT_ASMAP_FILENAME;
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
if(fs::exists(asmap_path)) {
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
} else {
// Last Resort: Check the parent directory
asmap_path = pwd / ".." / DEFAULT_ASMAP_FILENAME;
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
if(fs::exists(asmap_path)) {
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
} else {
// Mac SD
asmap_path = fs::path("/Applications/SilentDragon.app/Contents/MacOS/") / DEFAULT_ASMAP_FILENAME;
printf("%s: looking for asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: looking for asmap file at %s\n", __func__, asmap_path.string().c_str() );
if(fs::exists(asmap_path)) {
printf("%s: found asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: found asmap file at %s\n", __func__, asmap_path.string().c_str() );
} else {
// Shit is fucked up, die an honorable death
InitError(strprintf(_("Could not find any asmap file! Please report this bug to Hush Developers")));
// No asmap file found in any known location; abort startup.
InitError(strprintf(_("Could not find any asmap file! Please report this bug to DragonX Developers")));
return false;
}
}
@@ -1315,7 +1321,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!asmap_path.is_absolute()) {
asmap_path = GetDataDir() / asmap_path;
}
printf("%s: looking for custom asmap file at %s\n", __func__, asmap_path.c_str() );
LogPrint("net", "%s: looking for custom asmap file at %s\n", __func__, asmap_path.string().c_str() );
}
//TODO: verify asmap_path is not a directory
@@ -1329,7 +1335,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return false;
}
const uint256 asmap_version = SerializeHash(asmap);
printf("%s: asmap version=%s with %lu mappings\n", __func__, asmap_version.ToString().c_str(), asmap.size());
LogPrint("net", "%s: asmap version=%s with %lu mappings\n", __func__, asmap_version.ToString().c_str(), asmap.size());
LogPrintf("Using asmap version %s for IP bucketing with %lu mappings\n", asmap_version.ToString(), asmap.size());
addrman.m_asmap = std::move(asmap); // //node.connman->SetAsmap(std::move(asmap));
@@ -1348,20 +1354,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (SoftSetBoolArg("-rescan", true))
LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
}
//fprintf(stderr,"%s tik4\n", __FUNCTION__);
// Make sure enough file descriptors are available
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-allowbind"), 1);
nMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
//fprintf(stderr,"nMaxConnections %d\n",nMaxConnections);
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
fprintf(stderr,"nMaxConnections %d FD_SETSIZE.%d nBind.%d expr.%d \n",nMaxConnections,FD_SETSIZE,nBind,(int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS));
LogPrintf("nMaxConnections %d FD_SETSIZE.%d nBind.%d expr.%d \n",nMaxConnections,FD_SETSIZE,nBind,(int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS));
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections)
nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS;
//fprintf(stderr,"nMaxConnections %d\n",nMaxConnections);
// if using block pruning, then disable txindex
// also disable the wallet (for now, until SPV support is implemented in wallet)
if (GetArg("-prune", 0)) {
@@ -1407,10 +1410,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
if (find(categories.begin(), categories.end(), string("randomx")) != categories.end()) {
fRandomXDebug = true;
fprintf(stderr,"%s: enabled randomx debug\n", __func__);
LogPrintf("%s: enabled randomx debug\n", __func__);
}
//fprintf(stderr,"%s tik5\n", __FUNCTION__);
// Check for -debugnet
if (GetBoolArg("-debugnet", false))
InitWarning(_("Warning: Unsupported argument -debugnet ignored, use -debug=net."));
@@ -1430,7 +1432,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
mempool.setSanityCheck(1.0 / ratio);
}
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled = GetBoolArg("-checkpoints", true);
// Regtest inherits the DRAGONX *mainnet* checkpoint set (chainparams_commandline applies it
// for every SMART_CHAIN_SYMBOL=="DRAGONX" network, regardless of -regtest), whose top height
// ~3.2M would otherwise pin an isolated regtest chain in IsInitialBlockDownload() forever --
// disabling the ChainTip auto-ops (autoshield/sweep/consolidation) and the below-checkpoint
// script-check skip. Default checkpoints OFF on regtest so a fresh regtest node leaves IBD
// normally; still overridable with -checkpoints=1.
fCheckpointsEnabled = GetBoolArg("-checkpoints", chainparams.NetworkIDString() != "regtest");
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
@@ -1469,7 +1477,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("Bulk block streaming: %s\n", fBulkBlockSync ? "enabled" : "disabled");
fServer = GetBoolArg("-server", false);
//fprintf(stderr,"%s tik6\n", __FUNCTION__);
// block pruning; get the amount of disk space (in MB) to allot for block & undo files
int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024;
@@ -1557,7 +1564,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
expiryDelta = GetArg("-txexpirydelta", DEFAULT_TX_EXPIRY_DELTA);
bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", true);
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", false);
//fprintf(stderr,"%s tik7\n", __FUNCTION__);
std::string strWalletFile = GetArg("-wallet", "wallet.dat");
#endif // ENABLE_WALLET
@@ -1574,7 +1580,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
nLocalServices |= NODE_BLOOM;
}
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
//fprintf(stderr,"%s tik8\n", __FUNCTION__);
#ifdef ENABLE_MINING
if (mapArgs.count("-mineraddress")) {
@@ -1597,7 +1602,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
//fprintf(stderr,"%s tik9\n", __FUNCTION__);
if (!mapMultiArgs["-nuparams"].empty()) {
// Allow overriding network upgrade parameters for testing
if (Params().NetworkIDString() != "regtest") {
@@ -1646,10 +1650,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
std::string sha256_algo = SHA256AutoDetect();
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
//fprintf(stderr,"%s tik10\n", __FUNCTION__);
// Sanity check
if (!InitSanityCheck())
return InitError(_("Initialization sanity check failed. Please check for insanity. Hush is shutting down!"));
return InitError(_("Initialization sanity check failed. Please check for insanity. DragonX is shutting down!"));
std::string strDataDir = GetDataDir().string();
#ifdef ENABLE_WALLET
@@ -1657,14 +1660,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (strWalletFile != boost::filesystem::basename(strWalletFile) + boost::filesystem::extension(strWalletFile))
return InitError(strprintf(_("Wallet %s resides outside data directory %s"), strWalletFile, strDataDir));
#endif
// Make sure only a single Hush process is using the data directory.
// Make sure only a single DragonX process is using the data directory.
boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
if (file) fclose(file);
//fprintf(stderr,"%s tik11\n", __FUNCTION__);
fprintf(stderr,"Attempting to obtain lock %s\n", pathLockFile.string().c_str());
LogPrintf("Attempting to obtain lock %s\n", pathLockFile.string().c_str());
try {
static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
if (!lock.try_lock())
@@ -1679,10 +1681,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (GetBoolArg("-shrinkdebugfile", !fDebug))
ShrinkDebugFile();
//fprintf(stderr,"%s tik12\n", __FUNCTION__);
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("Hush version %s\n", FormatFullVersion());
LogPrintf("DragonX version %s\n", FormatFullVersion());
if (fPrintToDebugLog)
OpenDebugLog();
@@ -1712,7 +1713,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
threadGroup.create_thread(&ThreadRandomXVerify);
}
//fprintf(stderr,"%s tik13\n", __FUNCTION__);
// Start the lightweight task scheduler thread
CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);
@@ -1720,7 +1720,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Count uptime
MarkStartTime();
//fprintf(stderr,"%s tik14\n", __FUNCTION__);
if ((chainparams.NetworkIDString() != "regtest") &&
GetBoolArg("-showmetrics", 0) &&
@@ -1730,7 +1729,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
threadGroup.create_thread(&ThreadShowMetricsScreen);
}
//fprintf(stderr,"%s tik15\n", __FUNCTION__);
if ( HUSH_NSPV_FULLNODE )
{
@@ -1748,7 +1746,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!AppInitServers(threadGroup))
return InitError(_("Unable to start HTTP server. See debug log for details."));
}
//fprintf(stderr,"%s tik16\n", __FUNCTION__);
int64_t nStart;
@@ -1775,7 +1772,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#endif // ENABLE_WALLET
// ********************************************************* Step 6: network initialization
//fprintf(stderr,"%s tik17\n", __FUNCTION__);
RegisterNodeSignals(GetNodeSignals());
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1791,7 +1787,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError(strprintf("Total length of network version string %i exceeds maximum of %i characters. Reduce the number and/or size of uacomments.",
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
//fprintf(stderr,"%s tik18\n", __FUNCTION__);
// Disable clearnet peers if -clearnet=0 for this node or -ac_clearnet=0 for this chain
if (ASSETCHAINS_CLEARNET == 0 || !GetBoolArg("-clearnet", DEFAULT_CLEARNET)) {
@@ -1850,7 +1845,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
SetReachable(NET_IPV4, false);
}
//fprintf(stderr,"%s tik19\n", __FUNCTION__);
if (mapArgs.count("-allowlist")) {
BOOST_FOREACH(const std::string& net, mapMultiArgs["-allowlist"]) {
CSubNet subnet;
@@ -1913,7 +1907,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
fDiscover = GetBoolArg("-discover", true);
fNameLookup = GetBoolArg("-dns", true);
//fprintf(stderr,"%s tik22\n", __FUNCTION__);
bool fBound = false;
if (fListen) {
if (mapArgs.count("-bind") || mapArgs.count("-allowbind")) {
@@ -1952,7 +1945,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
//fprintf(stderr,"%s tik23\n", __FUNCTION__);
BOOST_FOREACH(const std::string& strDest, mapMultiArgs["-seednode"])
AddOneShot(strDest);
@@ -1988,7 +1980,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return !fRequestShutdown;
}
// ********************************************************* Step 7: load block chain
//fprintf(stderr,"%s tik24\n", __FUNCTION__);
fReindex = GetBoolArg("-reindex", false);
@@ -2053,7 +2044,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if ( checkval != fAddressIndex && fAddressIndex != 0 )
{
pblocktree->WriteFlag("addressindex", fAddressIndex);
fprintf(stderr,"set addressindex, will reindex. could take a while.\n");
LogPrintf("set addressindex, will reindex. could take a while.\n");
fReindex = true;
}
fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX);
@@ -2061,7 +2052,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if ( checkval != fSpentIndex && fSpentIndex != 0 )
{
pblocktree->WriteFlag("spentindex", fSpentIndex);
fprintf(stderr,"set spentindex, will reindex. could take a while.\n");
LogPrintf("set spentindex, will reindex. could take a while.\n");
fReindex = true;
}
}
@@ -2090,7 +2081,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
try {
pnotarizations = new NotarizationDB(100*1024*1024, false, fReindex);
pnotarizations = new NotarizationDB(NOTARIZATION_DB_CACHE_BYTES, false, fReindex);
} catch (const std::exception& e) {
// The notarizations (dPoW) DB is non-essential and node-regenerable. It has been seen to
// snapshot/flush torn (0-byte log -> leveldb "Database I/O error" on reopen), which
@@ -2106,7 +2097,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
boost::filesystem::remove_all(ndir.string() + ".corrupt");
boost::filesystem::rename(ndir, ndir.string() + ".corrupt");
}
pnotarizations = new NotarizationDB(100*1024*1024, false, true);
pnotarizations = new NotarizationDB(NOTARIZATION_DB_CACHE_BYTES, false, true);
}
@@ -2114,14 +2105,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
boost::filesystem::remove(GetDataDir() / "hushstate");
boost::filesystem::remove(GetDataDir() / "hushsignedmasks");
pblocktree->WriteReindexing(true);
fprintf(stderr, "%s: Deleted hushstate and hushsignedmasks...\n", __FUNCTION__);
LogPrintf("%s: Deleted hushstate and hushsignedmasks...\n", __FUNCTION__);
//If we're reindexing in prune mode, wipe away unusable block files and all undo data files
if (fPruneMode)
CleanupBlockRevFiles();
}
fprintf(stderr, "%s: Loading block index...\n", __FUNCTION__);
LogPrintf("%s: Loading block index...\n", __FUNCTION__);
if (!LoadBlockIndex()) {
strLoadError = _("Error loading block database");
break;
@@ -2145,7 +2136,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
break;
}
fprintf(stderr, "zindex=%s in block index\n", fZindex ? "enabled" : "disabled");
LogPrintf("zindex=%s in block index\n", fZindex ? "enabled" : "disabled");
if (fZindex != GetBoolArg("-zindex", false)) {
strLoadError = _("You need to rebuild the database using -reindex to change -zindex");
break;
@@ -2200,7 +2191,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!fLoaded) {
// first suggest a reindex
if (!fReset) {
fprintf(stderr,"%s: error in hd data\n", __FUNCTION__);
LogPrintf("%s: error in hd data\n", __FUNCTION__);
bool fRet = uiInterface.ThreadSafeMessageBox(
strLoadError + ".\n\n" + _("error in HDD data, might just need to update to latest, if that doesnt work, then you need to resync"),
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
@@ -2235,7 +2226,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
mempool.ReadFeeEstimates(est_filein);
fFeeEstimatesInitialized = true;
//fprintf(stderr,"%s tik25\n", __FUNCTION__);
// ********************************************************* Step 8: load wallet
#ifdef ENABLE_WALLET
@@ -2270,7 +2260,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (nLoadWalletRet != DB_LOAD_OK)
{
if (nLoadWalletRet == DB_CORRUPT)
strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n";
strErrors << _("Error loading wallet.dat: Wallet corrupted. If this wallet was last opened "
"by an older version, move wallet.dat aside and restore from your seed "
"phrase with -mnemonic=\"<your seed phrase>\" -rescan (see debug.log for "
"the specific record at fault).") << "\n";
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
{
string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data"
@@ -2278,10 +2271,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
InitWarning(msg);
}
else if (nLoadWalletRet == DB_TOO_NEW)
strErrors << _("Error loading wallet.dat: Wallet requires newer version of Hush") << "\n";
strErrors << _("Error loading wallet.dat: Wallet requires newer version of DragonX") << "\n";
else if (nLoadWalletRet == DB_NEED_REWRITE)
{
strErrors << _("Wallet needed to be rewritten: restart Hush to complete") << "\n";
strErrors << _("Wallet needed to be rewritten: restart DragonX to complete") << "\n";
LogPrintf("%s", strErrors.str());
return InitError(strErrors.str());
}
@@ -2404,7 +2397,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
int consolidationInterval = GetArg("-consolidationinterval", 25);
if (consolidationInterval < 5) {
fprintf(stderr,"%s: Invalid consolidation interval of %d < 5, setting to default of 25\n", __func__, consolidationInterval);
LogPrintf("%s: Invalid consolidation interval of %d < 5, setting to default of 25\n", __func__, consolidationInterval);
consolidationInterval = 25;
}
@@ -2429,7 +2422,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (pwalletMain->fSweepEnabled) {
int sweepInterval = GetArg("-zsweepinterval", 10);
if (sweepInterval < 5) {
fprintf(stderr,"%s: Invalid sweep interval of %d, setting to default of 10\n", __func__, sweepInterval);
LogPrintf("%s: Invalid sweep interval of %d, setting to default of 10\n", __func__, sweepInterval);
sweepInterval = 10;
}
pwalletMain->sweepInterval = sweepInterval;
@@ -2446,7 +2439,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
for (int i = 0; i < vSweep.size(); i++) {
// LogPrintf("Sweep Address: %s\n", vSweep[i]);
auto zSweep = DecodePaymentAddress(vSweep[i]);
if (!IsValidPaymentAddress(zSweep)) {
return InitError("Invalid zsweep address");
@@ -2515,10 +2507,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
"pass -autoshield=1 to enable.\n", __func__, pwalletMain->hdSeedOrigin);
}
if (pwalletMain->fAutoShieldEnabled) {
int autoShieldInterval = GetArg("-autoshieldinterval", 25);
if (autoShieldInterval < 5) {
fprintf(stderr,"%s: Invalid autoshield interval of %d < 5, setting to default of 25\n", __func__, autoShieldInterval);
autoShieldInterval = 25;
int autoShieldInterval = GetArg("-autoshieldinterval", DEFAULT_AUTOSHIELD_INTERVAL);
if (autoShieldInterval < MIN_AUTOSHIELD_INTERVAL) {
InitWarning(strprintf(_("autoshield interval %d below the minimum, clamping to %d"),
autoShieldInterval, MIN_AUTOSHIELD_INTERVAL));
autoShieldInterval = MIN_AUTOSHIELD_INTERVAL;
}
pwalletMain->autoShieldInterval = autoShieldInterval;
pwalletMain->nextAutoShield = pwalletMain->autoShieldInterval + chainActive.Height();
@@ -2527,16 +2520,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// guard against a fat-finger (e.g. -autoshieldfee=5000000000) that
// would otherwise build an over-fee or malformed shield tx that
// fails mempool admission every round.
CAmount autoShieldFee = GetArg("-autoshieldfee", 10000);
const CAmount AUTOSHIELD_MIN_FEE = 1000; // comfortably above minRelayTxFee for a small tx
const CAmount AUTOSHIELD_MAX_FEE = 10000000; // 0.1 DRGX; no sane autoshield fee exceeds this
CAmount autoShieldFee = GetArg("-autoshieldfee", DEFAULT_AUTOSHIELD_FEE);
if (autoShieldFee < AUTOSHIELD_MIN_FEE || autoShieldFee > AUTOSHIELD_MAX_FEE) {
fprintf(stderr,"%s: -autoshieldfee=%lld out of range [%lld,%lld], using default 10000\n",
__func__, (long long)autoShieldFee, (long long)AUTOSHIELD_MIN_FEE, (long long)AUTOSHIELD_MAX_FEE);
autoShieldFee = 10000;
InitWarning(strprintf(_("-autoshieldfee=%lld out of range [%lld,%lld], using default %lld"),
(long long)autoShieldFee, (long long)AUTOSHIELD_MIN_FEE, (long long)AUTOSHIELD_MAX_FEE, (long long)DEFAULT_AUTOSHIELD_FEE));
autoShieldFee = DEFAULT_AUTOSHIELD_FEE;
}
pwalletMain->autoShieldFee = autoShieldFee;
pwalletMain->autoShieldMinUtxos = GetArg("-autoshieldminutxos", 1);
pwalletMain->autoShieldMinUtxos = GetArg("-autoshieldminutxos", DEFAULT_AUTOSHIELD_MIN_UTXOS);
if (pwalletMain->autoShieldMinUtxos < 1) {
pwalletMain->autoShieldMinUtxos = 1;
}
@@ -2556,6 +2547,25 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError("Wallet must hold the spending key of -autoshieldaddress (else shielded coinbase would be unrecoverable)");
}
pwalletMain->autoShieldAddress = autoShieldAddress;
} else {
// No explicit destination. Resolve the seed-derived one now, read-only,
// so z_autoshieldstatus can say where coinbase will go BEFORE the first
// round rather than reporting an empty string until one fires. This
// never generates a key: a fresh account must not be a side effect of
// populating a status field. A brand-new wallet holds nothing in-gap
// yet, so the field stays empty and the RPC explains why.
LOCK(pwalletMain->cs_wallet);
if (!pwalletMain->IsLocked()) {
libzcash::SaplingPaymentAddress destAddr;
std::string destStr;
uint32_t destAccount = AUTOSHIELD_ACCOUNT_NONE;
if (ResolveAutoShieldDestinationReadOnly(destAddr, destStr, destAccount)
== AutoShieldDestStatus::Resolved) {
pwalletMain->autoShieldAddress = destStr;
LogPrintf("%s: autoshield destination %s (seed-derived sapling account %u)\n",
__func__, destStr, (unsigned)destAccount);
}
}
}
}
@@ -2669,10 +2679,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifdef ENABLE_MINING
#ifndef ENABLE_WALLET
if (GetBoolArg("-minetolocalwallet", false)) {
return InitError(_("Hush was not built with wallet support. Set -minetolocalwallet=0 to use -mineraddress, or rebuild Hush with wallet support."));
return InitError(_("DragonX was not built with wallet support. Set -minetolocalwallet=0 to use -mineraddress, or rebuild DragonX with wallet support."));
}
if (GetArg("-mineraddress", "").empty() && GetBoolArg("-gen", false)) {
return InitError(_("Hush was not built with wallet support. Set -mineraddress, or rebuild Hush with wallet support."));
return InitError(_("DragonX was not built with wallet support. Set -mineraddress, or rebuild DragonX with wallet support."));
}
#endif // !ENABLE_WALLET
@@ -2731,7 +2741,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Advertise willingness to SERVE bulk block streams (full nodes only) when opted in.
if ( fBulkBlockSync )
nLocalServices |= NODE_BULKBLOCKS;
fprintf(stderr,"nLocalServices %llx %d, %d\n",(long long)nLocalServices,GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),GetBoolArg("-spentindex", DEFAULT_SPENTINDEX));
LogPrintf("nLocalServices %llx %d, %d\n",(long long)nLocalServices,GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),GetBoolArg("-spentindex", DEFAULT_SPENTINDEX));
}
// ********************************************************* Step 10: import blocks
@@ -2747,7 +2757,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if ( !ActivateBestChain(true,state))
strErrors << "Failed to connect best block";
} else {
fprintf(stderr,"HUSH_REWIND < 0\n");
LogPrintf("HUSH_REWIND < 0\n");
}
std::vector<boost::filesystem::path> vImportFiles;
if (mapArgs.count("-loadblock"))
@@ -2776,7 +2786,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 11: start node
//fprintf(stderr,"Checking disk space...\n");
if (!CheckDiskSpace())
return false;
@@ -2814,7 +2823,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
SetRPCWarmupFinished();
if(fDebug)
fprintf(stderr,"RPC warmump finished\n");
fprintf(stderr,"RPC warmup finished\n");
uiInterface.InitMessage(_("Full Node Done Loading! :)"));
#ifdef ENABLE_WALLET

Binary file not shown.

View File

@@ -1259,11 +1259,10 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
// Ensure that a coinbase transaction is structured according to the consensus rules of the chain
bool ContextualCheckCoinbaseTransaction(int32_t slowflag,const CBlock *block,CBlockIndex * const previndex,const CTransaction& tx, const int nHeight,int32_t validateprices)
{
if ( slowflag != 0 && ASSETCHAINS_CBOPRET != 0 && validateprices != 0 && nHeight > 0 && tx.vout.size() > 0 )
{
if ( hush_opretvalidate(block,previndex,nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 )
return(false);
}
// The only coinbase-specific contextual check here was CBOPRET price-oracle
// validation (hush_opretvalidate), gated on ASSETCHAINS_CBOPRET, which is always
// 0 on DragonX (no -ac_cbopret). With that dead path removed there is nothing left
// to validate, so a DragonX coinbase is unconditionally valid at this stage.
return(true);
}

View File

@@ -160,7 +160,6 @@ bool hush_appendACscriptpub();
CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake)
{
//fprintf(stderr,"%s\n", __func__);
CScript scriptPubKeyIn(_scriptPubKeyIn);
CPubKey pk;
@@ -179,15 +178,13 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
uint32_t blocktime; const CChainParams& chainparams = Params();
bool fNotarizationBlock = false; std::vector<int8_t> NotarizationNotaries;
//fprintf(stderr,"%s: create new block with pubkey=%s\n", __func__, HexStr(pk).c_str());
// Create new block
if ( gpucount < 0 )
gpucount = HUSH_MAXGPUCOUNT;
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
//fprintf(stderr,"%s: created new block template\n", __func__);
if(!pblocktemplate.get())
{
fprintf(stderr,"%s: pblocktemplate.get() failure\n", __func__);
LogPrintf("%s: pblocktemplate.get() failure\n", __func__);
return NULL;
}
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
@@ -200,7 +197,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
pblock->vtx.push_back(CTransaction());
pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOps.push_back(-1); // updated at end
//fprintf(stderr,"%s: added dummy coinbase\n", __func__);
// Largest block you're willing to create:
unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE(1)); // MAX_BLOCK_SIZE(chainActive.LastTip()->GetHeight()+1));
@@ -217,7 +213,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
// until there are no more or the block reaches this size:
const unsigned int nBlockMinSize = std::min(nBlockMaxSize, (unsigned int) GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE));
// nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
//fprintf(stderr,"%s: nBlockMaxSize=%u, nBlockPrioritySize=%u, nBlockMinSize=%u\n", __func__, nBlockMaxSize, nBlockPrioritySize, nBlockMinSize);
// Collect memory pool transactions into the block
@@ -243,17 +238,18 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
uint32_t proposedTime = GetTime();
//fprintf(stderr,"%s: nHeight=%d, consensusBranchId=%u, proposedTime=%u\n", __func__, nHeight, consensusBranchId, proposedTime);
if (proposedTime == nMedianTimePast)
{
// too fast or stuck, this addresses the too fast issue, while moving
// forward as quickly as possible
for (int i; i < 100; i++)
for (int i = 0; i < 100; i++)
{
proposedTime = GetTime();
if (proposedTime == nMedianTimePast)
MilliSleep(10);
else
break; // time advanced past the median; stop waiting
}
}
pblock->nTime = GetTime();
@@ -280,7 +276,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
vector<TxPriority> vecPriority;
vecPriority.reserve(mempool.mapTx.size() + 1);
//fprintf(stderr,"%s: going to add txs from mempool\n", __func__);
// now add transactions from the mempool
int32_t Notarizations = 0; uint64_t txvalue;
uint32_t large_zins = 0; // number of ztxs with large number of inputs in block
@@ -299,7 +294,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight))
{
fprintf(stderr,"%s: coinbase.%d finaltx.%d expired.%d\n",__func__, tx.IsCoinBase(),IsFinalTx(tx, nHeight, nLockTimeCutoff),IsExpiredTx(tx, nHeight));
LogPrint("mempool", "%s: coinbase.%d finaltx.%d expired.%d\n",__func__, tx.IsCoinBase(),IsFinalTx(tx, nHeight, nLockTimeCutoff),IsExpiredTx(tx, nHeight));
continue;
}
txvalue = tx.GetValueOut();
@@ -378,9 +373,8 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
{
// check a notary didnt sign twice (this would be an invalid notarization later on and cause problems)
std::set<int> checkdupes( TMP_NotarizationNotaries.begin(), TMP_NotarizationNotaries.end() );
if ( checkdupes.size() != TMP_NotarizationNotaries.size() )
if ( checkdupes.size() != TMP_NotarizationNotaries.size() )
{
fprintf(stderr, "%s: WTFBBQ! possible notarization is signed multiple times by same notary, passed as normal transaction.\n", __func__);
} else fNotarization = true;
}
nTotalIn += tx.GetShieldedValueIn();
@@ -390,7 +384,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
// Priority is sum(valuein * age) / modified_txsize
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// fprintf(stderr,"%s: computing priority with nTxSize=%u\n", __func__, nTxSize);
dPriority = tx.ComputePriority(dPriority, nTxSize);
uint256 hash = tx.GetHash();
@@ -410,7 +403,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
Notarizations++;
if ( Notarizations > 1 )
{
fprintf(stderr, "%s: skipping notarization.%d\n",__func__, Notarizations);
LogPrint("mempool", "%s: skipping notarization.%d\n",__func__, Notarizations);
// Any attempted notarization needs to be in its own block!
continue;
}
@@ -421,7 +414,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
NotarizationNotaries = TMP_NotarizationNotaries;
dPriority = 1e16;
fNotarizationBlock = true;
//fprintf(stderr, "Notarization %s set to maximum priority\n",hash.ToString().c_str());
}
}
}
@@ -436,7 +428,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx())));
}
}
// fprintf(stderr,"%s: done adding txs from mempool\n", __func__);
// Collect transactions into block
int64_t interest;
@@ -448,7 +439,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
TxPriorityCompare comparer(fSortedByFee);
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
// fprintf(stderr,"%s: compared txs with fSortedByFee=%d\n", __func__, fSortedByFee);
while (!vecPriority.empty()) {
// Take highest priority transaction off the priority queue:
@@ -456,10 +446,8 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
CFeeRate feeRate = vecPriority.front().get<1>();
const CTransaction& tx = *(vecPriority.front().get<2>());
// fprintf(stderr,"%s: grabbed first tx from priority queue\n", __func__);
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
// fprintf(stderr,"%s: compared first tx from priority queue\n", __func__);
vecPriority.pop_back();
if(tx.vShieldedSpend.size() >= LARGE_ZINS_THRESHOLD && large_zins >= LARGE_ZINS_MAX) {
@@ -476,12 +464,11 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
// Size limits
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// fprintf(stderr,"%s: nTxSize = %u\n", __func__, nTxSize);
if (nBlockSize + nTxSize >= nBlockMaxSize-512) // room for extra autotx
{
fprintf(stderr,"%s: nBlockSize %d + %d nTxSize >= %d nBlockMaxSize\n",__func__, (int32_t)nBlockSize,(int32_t)nTxSize,(int32_t)nBlockMaxSize);
LogPrint("mempool", "%s: nBlockSize %d + %d nTxSize >= %d nBlockMaxSize\n",__func__, (int32_t)nBlockSize,(int32_t)nTxSize,(int32_t)nBlockMaxSize);
continue;
}
@@ -489,11 +476,9 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
unsigned int nTxSigOps = GetLegacySigOpCount(tx);
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS-1)
{
//fprintf(stderr,"A nBlockSigOps %d + %d nTxSigOps >= %d MAX_BLOCK_SIGOPS-1\n",(int32_t)nBlockSigOps,(int32_t)nTxSigOps,(int32_t)MAX_BLOCK_SIGOPS);
continue;
}
// fprintf(stderr,"%s: looking to see if we need to skip any fee=0 txs\n", __func__);
// Skip free transactions if we're past the minimum block size:
const uint256& hash = tx.GetHash();
@@ -502,7 +487,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
{
fprintf(stderr,"%s: fee rate skip\n", __func__);
LogPrint("mempool", "%s: fee rate skip\n", __func__);
continue;
}
// Prioritize by fee once past the priority size or we run out of high-priority transactions
@@ -516,7 +501,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
if (!view.HaveInputs(tx))
{
//fprintf(stderr,"dont have inputs\n");
continue;
}
CAmount nTxFees = view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime)-tx.GetValueOut();
@@ -541,7 +525,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
opcodetype op;
std::vector<uint8_t> opretData;
if (txout.scriptPubKey.GetOp(it, op, opretData)) {
//std::cerr << HexStr(opretData.begin(), opretData.end()) << std::endl;
nTxOpretSize += opretData.size();
}
}
@@ -552,13 +535,11 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
std::cerr << __func__ << ": " << tx.GetHash().ToString() << " nTxSize=" << nTxSize << " nTxOpretSize=" << nTxOpretSize << " feeRate=" << feeRate.ToString() << " opretMinFee=" << opretMinFee << " nTxFees=" << nTxFees <<" fSpamTx=" << fSpamTx << std::endl;
continue;
}
// std::cerr << tx.GetHash().ToString() << " vecPriority.size() = " << vecPriority.size() << std::endl;
}
nTxSigOps += GetP2SHSigOpCount(tx, view);
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS-1)
{
//fprintf(stderr,"B nBlockSigOps %d + %d nTxSigOps >= %d MAX_BLOCK_SIGOPS-1\n",(int32_t)nBlockSigOps,(int32_t)nTxSigOps,(int32_t)MAX_BLOCK_SIGOPS);
continue;
}
// Note that flags: we don't want to set mempool/IsStandard()
@@ -568,7 +549,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
PrecomputedTransactionData txdata(tx);
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
{
fprintf(stderr,"%s: ContextualCheckInputs failure\n",__func__);
LogPrint("mempool", "%s: ContextualCheckInputs failure\n",__func__);
continue;
}
UpdateCoins(tx, view, nHeight);
@@ -623,13 +604,11 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
// fprintf(stderr,"%s: nLastBlockTx=%lu , nLastBlockSize=%lu\n", __func__, nLastBlockTx, nLastBlockSize);
if ( ASSETCHAINS_ADAPTIVEPOW <= 0 )
blocktime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetTime());
else blocktime = 1 + std::max((int64_t)(pindexPrev->nTime+1), GetTime());
//pblock->nTime = blocktime + 1;
// fprintf(stderr,"%s: calling GetNextWorkRequired\n", __func__);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits);
@@ -643,7 +622,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees;
// fprintf(stderr,"%s: mine ht.%d with %.8f\n",__func__,nHeight,(double)txNew.vout[0].nValue/COIN);
txNew.nExpiryHeight = 0;
if ( ASSETCHAINS_ADAPTIVEPOW <= 0 )
txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetTime());
@@ -665,10 +643,9 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
static bool didinit = false;
if ( !didinit && nHeight > HUSH_EARLYTXID_HEIGHT && HUSH_EARLYTXID != zeroid && hush_appendACscriptpub() )
{
fprintf(stderr, "appended ccopreturn to assetchains_scriptpub.%s\n", assetchains_scriptpub.c_str());
LogPrintf("appended ccopreturn to assetchains_scriptpub.%s\n", assetchains_scriptpub.c_str());
didinit = true;
}
//fprintf(stderr,"mine to -ac_script\n");
//txNew.vout[1].scriptPubKey = CScript() << ParseHex();
int32_t len = strlen(assetchains_scriptpub.c_str());
len >>= 1;
@@ -682,14 +659,11 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
for (i=0; i<33; i++)
{
ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i];
//fprintf(stderr,"%02x",ptr[i+1]);
}
ptr[34] = OP_CHECKSIG;
//fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n");
}
//printf("autocreate commision vout\n");
} else if ( (uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE) {
fprintf(stderr,"timelocked chains not supported in this code!\n");
LogPrintf("timelocked chains not supported in this code!\n");
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
return(0);
@@ -702,16 +676,15 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
uint64_t totalsats = hush_notarypay(txNew, NotarizationNotaries, pblock->nTime, nHeight, script, scriptlen);
if ( totalsats == 0 )
{
fprintf(stderr, "Could not create notary payment, trying again.\n");
if ( !isStake )
{
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
}
LogPrintf("Could not create notary payment, trying again.\n");
// Release unconditionally to match the unconditional ENTER above. The old
// `if(!isStake)` guard leaked cs_main/mempool.cs on the isStake path (this
// still return(0)s), while the success and timelock paths always release.
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
return(0);
}
//fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats);
} else fprintf(stderr, "vout 2 of notarization is not OP_RETURN scriptlen.%i\n", scriptlen);
} else LogPrintf("vout 2 of notarization is not OP_RETURN scriptlen.%i\n", scriptlen);
}
if ( ASSETCHAINS_CBOPRET != 0 )
{
@@ -719,7 +692,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
txNew.vout.resize(numv+1);
txNew.vout[numv].nValue = 0;
txNew.vout[numv].scriptPubKey = hush_mineropret(nHeight);
//printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size());
}
pblock->vtx[0] = txNew;
pblocktemplate->vTxFees[0] = -nFees;
@@ -752,26 +724,23 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
if ( ASSETCHAINS_CC == 0 && pindexPrev != 0 && (IS_HUSH_NOTARY == 0 || My_notaryid < 0) )
{
CValidationState state;
//fprintf(stderr,"%s: check validity\n", __func__);
if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) // invokes CC checks
{
if ( !isStake )
{
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
}
fprintf(stderr,"%s: TestBlockValidity failed!\n", __func__);
// Release unconditionally to match the unconditional ENTER above. The old
// `if(!isStake)` guard leaked cs_main/mempool.cs on the isStake path (this
// still return(0)s), while the success and timelock paths always release.
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
LogPrintf("%s: TestBlockValidity failed!\n", __func__);
//throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed"); // crashes the node, moved to GetBlockTemplate and issue return.
return(0);
}
//fprintf(stderr,"valid\n");
}
}
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
// fprintf(stderr,"%s: done\n", __func__);
return pblocktemplate.release();
}
@@ -781,7 +750,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
{
//fprintf(stderr,"RandomXMiner: %s with nExtraNonce=%u\n", __func__, nExtraNonce);
// Update nExtraNonce
static uint256 hashPrevBlock;
if (hashPrevBlock != pblock->hashPrevBlock)
@@ -805,7 +773,6 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, int32_t gpucount, bool isStake)
{
CPubKey pubkey; CScript scriptPubKey; uint8_t *script,*ptr; int32_t i,len;
// fprintf(stderr,"%s: with nHeight=%d\n", __func__, nHeight);
// Create a local variable instead of modifying the global assetchains_scriptpub
auto assetchains_scriptpub = devtax_scriptpub_for_height(nHeight);
@@ -815,7 +782,6 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
{
pubkey = ParseHex(ASSETCHAINS_OVERRIDE_PUBKEY);
scriptPubKey = CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG;
// fprintf(stderr,"%s: with pubkey=%s\n", __func__, HexStr(pubkey).c_str() );
} else {
len = strlen(assetchains_scriptpub.c_str());
len >>= 1;
@@ -824,7 +790,6 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
decode_hex(ptr,len,(char *)assetchains_scriptpub.c_str());
}
} else if ( USE_EXTERNAL_PUBKEY != 0 ) {
//fprintf(stderr,"use notary pubkey\n");
pubkey = ParseHex(NOTARY_PUBKEY);
scriptPubKey = CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG;
} else {
@@ -845,14 +810,13 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
// scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
scriptPubKey = GetScriptForDestination(dest);
Getscriptaddress(destaddr,scriptPubKey);
fprintf(stderr,"%s: wallet disabled with mineraddress=%s\n", __func__, destaddr);
LogPrintf("%s: wallet disabled with mineraddress=%s\n", __func__, destaddr);
} else {
return NULL;
}
}
}
}
// fprintf(stderr,"%s: calling CreateNewBlock\n", __func__);
return CreateNewBlock(pubkey, scriptPubKey, gpucount, isStake);
}
@@ -866,7 +830,6 @@ void hush_sendmessage(int32_t minpeers,int32_t maxpeers,const char *message,std:
continue;
if ( numsent < minpeers || (rand() % 10) == 0 )
{
//fprintf(stderr,"pushmessage\n");
pnode->PushMessage(message,payload);
if ( numsent++ > maxpeers )
break;
@@ -887,16 +850,6 @@ static bool ProcessBlockFound(CBlock* pblock)
LOCK(cs_main);
if (pblock->hashPrevBlock != chainActive.LastTip()->GetBlockHash())
{
uint256 hash; int32_t i;
hash = pblock->hashPrevBlock;
for (i=31; i>=0; i--)
fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
fprintf(stderr," <- prev (stale)\n");
hash = chainActive.LastTip()->GetBlockHash();
for (i=31; i>=0; i--)
fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
fprintf(stderr," <- chainTip (stale)\n");
return error("HushMiner: generated block is stale");
}
}
@@ -914,7 +867,6 @@ static bool ProcessBlockFound(CBlock* pblock)
}
}
#endif
//fprintf(stderr,"process new block\n");
// Process this block the same as if we had received it from another node
CValidationState state;
@@ -989,9 +941,7 @@ CBlockIndex *get_chainactive(int32_t height)
LOCK(cs_main);
return(chainActive[height]);
}
// else fprintf(stderr,"get_chainactive height %d > active.%d\n",height,chainActive.Tip()->GetHeight());
}
//fprintf(stderr,"get_chainactive null chainActive.Tip() height %d\n",height);
return(0);
}
@@ -1040,7 +990,7 @@ static void LogProcessMemory(const char* label) {
PMC_EX pmc = {};
pmc.cb = sizeof(pmc);
if (pfn(GetCurrentProcess(), &pmc, sizeof(pmc))) {
LogPrintf("MemDiag [%s]: WorkingSet=%.1fMB, PrivateUsage=%.1fMB, PagefileUsage=%.1fMB\n",
LogPrint("randomx", "MemDiag [%s]: WorkingSet=%.1fMB, PrivateUsage=%.1fMB, PagefileUsage=%.1fMB\n",
label,
pmc.WorkingSetSize / (1024.0 * 1024.0),
pmc.PrivateUsage / (1024.0 * 1024.0),
@@ -1058,7 +1008,7 @@ static void LogProcessMemory(const char* label) {
if (strncmp(line, "VmRSS:", 6) == 0 || strncmp(line, "VmSize:", 7) == 0) {
// Remove newline
line[strlen(line)-1] = '\0';
LogPrintf("MemDiag [%s]: %s\n", label, line);
LogPrint("randomx", "MemDiag [%s]: %s\n", label, line);
}
}
fclose(f);
@@ -1087,7 +1037,7 @@ struct RandomXDatasetManager {
if (initialized) return true;
flags |= RANDOMX_FLAG_FULL_MEM;
LogPrintf("RandomXDatasetManager: flags=0x%x (JIT=%d, HARD_AES=%d, FULL_MEM=%d, LARGE_PAGES=%d)\n",
LogPrint("randomx", "RandomXDatasetManager: flags=0x%x (JIT=%d, HARD_AES=%d, FULL_MEM=%d, LARGE_PAGES=%d)\n",
(int)flags,
!!(flags & RANDOMX_FLAG_JIT), !!(flags & RANDOMX_FLAG_HARD_AES),
!!(flags & RANDOMX_FLAG_FULL_MEM), !!(flags & RANDOMX_FLAG_LARGE_PAGES));
@@ -1128,11 +1078,11 @@ struct RandomXDatasetManager {
// Log the actual memory addresses to help diagnose sharing issues
uint8_t *datasetMemory = (uint8_t*)randomx_get_dataset_memory(dataset);
size_t datasetSize = datasetItemCount * RANDOMX_DATASET_ITEM_SIZE;
LogPrintf("RandomXDatasetManager: allocated shared dataset:\n");
LogPrintf(" - Dataset struct at: %p\n", (void*)dataset);
LogPrintf(" - Dataset memory at: %p (size: %.2f GB)\n", (void*)datasetMemory, datasetSize / (1024.0 * 1024.0 * 1024.0));
LogPrintf(" - Items: %lu, Item size: %d bytes\n", datasetItemCount, RANDOMX_DATASET_ITEM_SIZE);
LogPrintf(" - Expected total process memory: ~%.2f GB + ~2MB per mining thread\n", datasetSize / (1024.0 * 1024.0 * 1024.0));
LogPrintf("RandomXDatasetManager: allocated shared dataset (%.2f GB, %lu items)\n",
datasetSize / (1024.0 * 1024.0 * 1024.0), datasetItemCount);
LogPrint("randomx", " - Dataset struct at: %p, memory at: %p\n", (void*)dataset, (void*)datasetMemory);
LogPrint("randomx", " - Item size: %d bytes; expected ~%.2f GB + ~2MB per mining thread\n",
RANDOMX_DATASET_ITEM_SIZE, datasetSize / (1024.0 * 1024.0 * 1024.0));
return true;
}
@@ -1190,9 +1140,9 @@ struct RandomXDatasetManager {
if (vm != nullptr) {
int id = ++vmCount;
uint8_t *datasetMemory = (uint8_t*)randomx_get_dataset_memory(dataset);
LogPrintf("RandomXDatasetManager: VM #%d created — VM at %p, shared dataset at %p\n",
LogPrint("randomx", "RandomXDatasetManager: VM #%d created — VM at %p, shared dataset at %p\n",
id, (void*)vm, (void*)datasetMemory);
LogPrintf(" Per-thread overhead: ~2MB scratchpad + ~84KB JIT (dataset NOT copied)\n");
LogPrint("randomx", " Per-thread overhead: ~2MB scratchpad + ~84KB JIT (dataset NOT copied)\n");
LogProcessMemory("after CreateVM");
}
return vm;
@@ -1279,13 +1229,11 @@ void static RandomXMiner()
randomx_vm *myVM = nullptr;
try {
// fprintf(stderr,"RandomXMiner: mining %s with randomx\n",SMART_CHAIN_SYMBOL);
rxdebug("%s: mining %s with randomx\n", SMART_CHAIN_SYMBOL);
while (true)
{
// fprintf(stderr,"RandomXMiner: beginning mining loop on %s with nExtraNonce=%u\n",SMART_CHAIN_SYMBOL, nExtraNonce);
rxdebug("%s: start mining loop on %s with nExtraNonce=%u\n", SMART_CHAIN_SYMBOL, nExtraNonce);
if (chainparams.MiningRequiresPeers()) {
@@ -1303,10 +1251,8 @@ void static RandomXMiner()
if (!fvNodesEmpty )//&& !IsInitialBlockDownload())
break;
MilliSleep(15000);
//fprintf(stderr,"fvNodesEmpty %d IsInitialBlockDownload(%s) %d\n",(int32_t)fvNodesEmpty,SMART_CHAIN_SYMBOL,(int32_t)IsInitialBlockDownload());
} while (true);
//fprintf(stderr,"%s Found peers\n",SMART_CHAIN_SYMBOL);
miningTimer.start();
}
@@ -1320,7 +1266,7 @@ void static RandomXMiner()
// If we don't have a valid chain tip to work from, wait and try again.
if (pindexPrev == nullptr) {
fprintf(stderr,"%s: null pindexPrev, trying again...\n",__func__);
LogPrint("randomx", "%s: null pindexPrev, trying again...\n",__func__);
MilliSleep(1000);
continue;
}
@@ -1331,7 +1277,6 @@ void static RandomXMiner()
Mining_start = (uint32_t)time(NULL);
}
// fprintf(stderr,"RandomXMiner: using initial key with interval=%d and lag=%d\n", randomxInterval, randomxBlockLag);
rxdebug("%s: using initial key, interval=%d, lag=%d, Mining_height=%u\n", randomxInterval, randomxBlockLag, Mining_height);
// Update the shared dataset key — only one thread will actually rebuild,
// others will see the key is already current and skip.
@@ -1364,14 +1309,12 @@ void static RandomXMiner()
// Acquire shared lock to prevent dataset rebuild while we're hashing
boost::shared_lock<boost::shared_mutex> datasetLock(g_rxDatasetManager->datasetMtx);
//fprintf(stderr,"RandomXMiner: Mining_start=%u\n", Mining_start);
#ifdef ENABLE_WALLET
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, pindexPrev->GetHeight()+1, gpucount, 0);
#else
CBlockTemplate *ptr = CreateNewBlockWithKey();
#endif
// fprintf(stderr,"RandomXMiner: created new block with Mining_start=%u\n",Mining_start);
rxdebug("%s: created new block with Mining_start=%u\n",Mining_start);
if ( ptr == 0 )
{
@@ -1384,11 +1327,10 @@ void static RandomXMiner()
}
static uint32_t counter;
if ( counter++ < 10 )
fprintf(stderr,"RandomXMiner: created illegal blockB, retry with counter=%u\n", counter);
LogPrint("randomx", "RandomXMiner: created illegal blockB, retry with counter=%u\n", counter);
sleep(1);
continue;
}
// fprintf(stderr,"RandomXMiner: getting block template\n");
rxdebug("%s: getting block template\n");
unique_ptr<CBlockTemplate> pblocktemplate(ptr);
@@ -1410,14 +1352,13 @@ void static RandomXMiner()
{
static uint32_t counter;
if ( counter++ < 10 )
fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",SMART_CHAIN_SYMBOL);
LogPrint("randomx", "skip generating %s on-demand block, no tx avail\n",SMART_CHAIN_SYMBOL);
sleep(10);
continue;
} else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",SMART_CHAIN_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
} else LogPrint("randomx", "%s vouts.%d mining.%d vs %d\n",SMART_CHAIN_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
}
rxdebug("%s: incrementing extra nonce\n");
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
// fprintf(stderr,"RandomXMiner: %u transactions in block\n",(int32_t)pblock->vtx.size());
LogPrintf("Running HushRandomXMiner with %u transactions in block (%u bytes)\n",pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION));
// Search
@@ -1433,12 +1374,11 @@ void static RandomXMiner()
while (true)
{
if ( gotinvalid != 0 ) {
fprintf(stderr,"RandomXMiner: gotinvalid=%d\n",gotinvalid);
LogPrint("randomx", "RandomXMiner: gotinvalid=%d\n",gotinvalid);
break;
}
hush_longestchain();
// fprintf(stderr,"RandomXMiner: solving with nNonce = %s\n",pblock->nNonce.ToString().c_str());
rxdebug("%s: solving with nNonce = %s\n",pblock->nNonce.ToString().c_str());
arith_uint256 hashTarget;
hashTarget = HASHTarget;
@@ -1448,8 +1388,6 @@ void static RandomXMiner()
// Serialize block header without nSolution but with nNonce for deterministic RandomX input
randomxInput << rxInput;
// std::cerr << "RandomXMiner: randomxInput=" << HexStr(randomxInput) << "\n";
// fprintf(stderr,"RandomXMiner: created randomxKey=%s , randomxInput.size=%lu\n", randomxKey, randomxInput.size() ); //randomxInput);
rxdebug("%s: randomxKey=%s randomxInput=%s\n", randomxKey, HexStr(randomxInput).c_str());
rxdebug("%s: calculating randomx hash\n");
@@ -1478,7 +1416,6 @@ void static RandomXMiner()
rxdebug("%s: Checking solution against target\n");
pblock->nSolution = soln;
solutionTargetChecks.increment();
// fprintf(stderr,"%s: solutionTargetChecks=%lu\n", __func__, solutionTargetChecks.get());
B = *pblock;
h = UintToArith256(B.GetHash());
@@ -1508,17 +1445,6 @@ void static RandomXMiner()
SetSkipRandomXValidation(false);
if ( !fValid )
{
h = UintToArith256(B.GetHash());
fprintf(stderr,"RandomXMiner: TestBlockValidity FAILED at ht.%d nNonce=%s hash=",
Mining_height, pblock->nNonce.ToString().c_str());
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
fprintf(stderr," nSolution.size=%lu\n", B.nSolution.size());
// Dump nSolution hex for comparison with validator
fprintf(stderr,"RandomXMiner: nSolution=");
for (unsigned i = 0; i < B.nSolution.size(); i++)
fprintf(stderr,"%02x", B.nSolution[i]);
fprintf(stderr,"\n");
LogPrintf("RandomXMiner: TestBlockValidity FAILED at ht.%d, gotinvalid=1, state=%s\n",
Mining_height, state.GetRejectReason());
gotinvalid = 1;
@@ -1575,13 +1501,13 @@ void static RandomXMiner()
{
if ( Mining_height > ASSETCHAINS_MINHEIGHT )
{
fprintf(stderr,"%s: no nodes, break\n", __func__);
LogPrint("randomx", "%s: no nodes, break\n", __func__);
break;
}
}
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
{
fprintf(stderr,"%s: nonce & 0xffff == 0xffff, break\n", __func__);
LogPrint("randomx", "%s: nonce & 0xffff == 0xffff, break\n", __func__);
break;
}
// Update nNonce and nTime
@@ -1604,7 +1530,6 @@ void static RandomXMiner()
LogPrintf("%s: destroyed vm via thread interrupt\n", __func__);
} else {
LogPrintf("%s: WARNING myVM already null in thread interrupt handler, skipping destroy (would double-free)\n", __func__);
fprintf(stderr, "%s: WARNING myVM already null in thread interrupt, would have double-freed!\n", __func__);
}
// Dataset and cache are owned by g_rxDatasetManager — do NOT release here
@@ -1613,7 +1538,7 @@ void static RandomXMiner()
} catch (const std::runtime_error &e) {
miningTimer.stop();
c.disconnect();
fprintf(stderr,"RandomXMiner: runtime error: %s\n", e.what());
LogPrintf("RandomXMiner: runtime error: %s\n", e.what());
if (myVM != nullptr) {
randomx_destroy_vm(myVM);
@@ -1672,7 +1597,7 @@ void static BitcoinMiner()
assert(solver == "tromp" || solver == "default");
LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k);
if ( SMART_CHAIN_SYMBOL[0] != 0 )
fprintf(stderr,"notaryid.%d Mining.%s with %s\n",notaryid,SMART_CHAIN_SYMBOL,solver.c_str());
LogPrintf("notaryid.%d Mining.%s with %s\n",notaryid,SMART_CHAIN_SYMBOL,solver.c_str());
std::mutex m_cs;
bool cancelSolver = false;
boost::signals2::connection c = uiInterface.NotifyBlockTip.connect(
@@ -1685,7 +1610,7 @@ void static BitcoinMiner()
try {
if ( SMART_CHAIN_SYMBOL[0] != 0 )
fprintf(stderr,"try %s Mining with %s\n",SMART_CHAIN_SYMBOL,solver.c_str());
LogPrintf("try %s Mining with %s\n",SMART_CHAIN_SYMBOL,solver.c_str());
while (true)
{
if (chainparams.MiningRequiresPeers()) {
@@ -1703,10 +1628,8 @@ void static BitcoinMiner()
if (!fvNodesEmpty )//&& !IsInitialBlockDownload())
break;
MilliSleep(15000);
//fprintf(stderr,"fvNodesEmpty %d IsInitialBlockDownload(%s) %d\n",(int32_t)fvNodesEmpty,SMART_CHAIN_SYMBOL,(int32_t)IsInitialBlockDownload());
} while (true);
//fprintf(stderr,"%s Found peers\n",SMART_CHAIN_SYMBOL);
miningTimer.start();
}
//
@@ -1717,7 +1640,7 @@ void static BitcoinMiner()
// If we don't have a valid chain tip to work from, wait and try again.
if (pindexPrev == nullptr) {
fprintf(stderr,"%s: null pindexPrev, trying again...\n",__func__);
LogPrint("pow", "%s: null pindexPrev, trying again...\n",__func__);
MilliSleep(1000);
continue;
}
@@ -1729,7 +1652,6 @@ void static BitcoinMiner()
}
if ( SMART_CHAIN_SYMBOL[0] != 0 && ASSETCHAINS_STAKED == 0 )
{
//fprintf(stderr,"%s create new block ht.%d\n",SMART_CHAIN_SYMBOL,Mining_height);
//sleep(3);
}
@@ -1750,11 +1672,10 @@ void static BitcoinMiner()
}
static uint32_t counter;
if ( counter++ < 10 && ASSETCHAINS_STAKED == 0 )
fprintf(stderr,"created illegal blockB, retry\n");
LogPrint("pow", "created illegal blockB, retry\n");
sleep(1);
continue;
}
//fprintf(stderr,"get template\n");
unique_ptr<CBlockTemplate> pblocktemplate(ptr);
if (!pblocktemplate.get())
{
@@ -1775,14 +1696,13 @@ void static BitcoinMiner()
{
static uint32_t counter;
if ( counter++ < 10 )
fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",SMART_CHAIN_SYMBOL);
LogPrint("pow", "skip generating %s on-demand block, no tx avail\n",SMART_CHAIN_SYMBOL);
sleep(10);
continue;
} else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",SMART_CHAIN_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
} else LogPrint("pow", "%s vouts.%d mining.%d vs %d\n",SMART_CHAIN_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
}
}
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
//fprintf(stderr,"Running HushMiner.%s with %u transactions in block\n",solver.c_str(),(int32_t)pblock->vtx.size());
LogPrintf("Running HushMiner.%s with %u transactions in block (%u bytes)\n",solver.c_str(),pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION));
// Search
@@ -1798,7 +1718,6 @@ void static BitcoinMiner()
gotinvalid = 0;
while (true)
{
//fprintf(stderr,"gotinvalid.%d\n",gotinvalid);
if ( gotinvalid != 0 )
break;
hush_longestchain();
@@ -1823,7 +1742,6 @@ void static BitcoinMiner()
if ( HUSH_MININGTHREADS > 0 && ASSETCHAINS_STAKED > 0 && ASSETCHAINS_STAKED < 100 && Mining_height > 10 )
hashTarget = HASHTarget_POW;
//else if ( ASSETCHAINS_ADAPTIVEPOW > 0 )
// hashTarget = HASHTarget_POW;
else hashTarget = HASHTarget;
std::function<bool(std::vector<unsigned char>)> validBlock =
#ifdef ENABLE_WALLET
@@ -1837,7 +1755,6 @@ void static BitcoinMiner()
LogPrint("pow", "- Checking solution against target\n");
pblock->nSolution = soln;
solutionTargetChecks.increment();
// fprintf(stderr, "%s: solutionTargetChecks=%lu\n", __func__, solutionTargetChecks.get());
B = *pblock;
h = UintToArith256(B.GetHash());
/*for (z=31; z>=16; z--)
@@ -1857,13 +1774,12 @@ void static BitcoinMiner()
}
if ( IS_HUSH_NOTARY != 0 && B.nTime > GetTime() )
{
//fprintf(stderr,"need to wait %d seconds to submit block\n",(int32_t)(B.nTime - GetTime()));
while ( GetTime() < B.nTime-2 )
{
sleep(1);
if ( chainActive.LastTip()->GetHeight() >= Mining_height )
{
fprintf(stderr,"new block arrived\n");
LogPrint("pow", "new block arrived\n");
return(false);
}
}
@@ -1877,13 +1793,6 @@ void static BitcoinMiner()
MilliSleep((rand() % (r * 1000)) + 1000);
}
}
else
{
uint256 tmp = B.GetHash();
int32_t z; for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&tmp)[z]);
fprintf(stderr," mined %s block %d!\n",SMART_CHAIN_SYMBOL,Mining_height);
}
CValidationState state;
//{ LOCK(cs_main);
@@ -1891,8 +1800,6 @@ void static BitcoinMiner()
{
h = UintToArith256(B.GetHash());
//for (z=31; z>=0; z--)
// fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
//fprintf(stderr," Invalid block mined, try again\n");
gotinvalid = 1;
return(false);
}
@@ -1967,8 +1874,6 @@ void static BitcoinMiner()
if (found) {
int32_t i; uint256 hash = pblock->GetHash();
//for (i=0; i<32; i++)
// fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
//fprintf(stderr," <- %s Block found %d\n",SMART_CHAIN_SYMBOL,Mining_height);
//FOUND_BLOCK = 1;
//HUSH_MAYBEMINED = Mining_height;
break;
@@ -1993,14 +1898,14 @@ void static BitcoinMiner()
{
if ( SMART_CHAIN_SYMBOL[0] == 0 || Mining_height > ASSETCHAINS_MINHEIGHT )
{
fprintf(stderr,"no nodes, break\n");
LogPrint("pow", "no nodes, break\n");
break;
}
}
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
{
//if ( 0 && SMART_CHAIN_SYMBOL[0] != 0 )
fprintf(stderr,"0xffff, break\n");
LogPrint("pow", "0xffff, break\n");
break;
}
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
@@ -2024,7 +1929,6 @@ void static BitcoinMiner()
HASHTarget.SetCompact(pblock->nBits);
hashTarget = HASHTarget;
savebits = pblock->nBits;
//hashTarget = HASHTarget_POW = hush_adaptivepow_target(Mining_height,HASHTarget,pblock->nTime);
}
/*if ( NOTARY_PUBKEY33[0] == 0 )
{
@@ -2104,7 +2008,6 @@ void static BitcoinMiner()
g_rxDatasetManager = new RandomXDatasetManager();
if (!g_rxDatasetManager->Init()) {
LogPrintf("%s: FATAL - Failed to initialize shared RandomX dataset manager\n", __func__);
fprintf(stderr, "%s: FATAL - Failed to initialize shared RandomX dataset manager\n", __func__);
delete g_rxDatasetManager;
g_rxDatasetManager = nullptr;
delete minerThreads;

View File

@@ -56,7 +56,7 @@ extern uint8_t ASSETCHAINS_CLEARNET;
// Run asmap health check every 24hr by default
#define ASMAP_HEALTHCHECK_INTERVAL 24*60*60
// This is every 2 blocks, on avg, on HUSH3
// Interval (seconds) between zindex stat dumps when -zindex is enabled.
#define DUMP_ZINDEX_INTERVAL 150
#define CHECK_PLZ_STOP_INTERVAL 120
@@ -79,7 +79,9 @@ extern uint8_t ASSETCHAINS_CLEARNET;
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
#define USE_TLS "encrypted as fuck"
// Marker macro that enables the TLS p2p transport. Only its definedness is
// ever tested (via defined()/#ifdef); the string value itself is never used.
#define USE_TLS "enabled"
#if defined(USE_TLS) && !defined(TLS1_3_VERSION)
// minimum secure protocol is 1.3
@@ -468,12 +470,20 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) {
bool connected = false;
std::unique_ptr<Sock> sock;
if (!addrConnect.IsValid()) {
return NULL;
}
// When connecting by name (pszDest is set, e.g. -connect / -addnode host:port
// or "addnode <host> onetry"), addrConnect is an empty placeholder — the real
// target is resolved from pszDest by ConnectSocketByName() below. Only validate
// addrConnect when we are dialing it directly (pszDest == NULL); otherwise
// IsValid()/IsReachable() on the empty address abort the connection before it is
// ever attempted, which silently breaks -connect.
if (!pszDest) {
if (!addrConnect.IsValid()) {
return NULL;
}
if (!IsReachable(addrConnect)) {
return NULL;
if (!IsReachable(addrConnect)) {
return NULL;
}
}
if (addrConnect.GetNetwork() == NET_I2P && m_i2p_sam_session.get() != nullptr) {
@@ -614,7 +624,7 @@ void DumpBanlist()
if (bandb.Write(banmap)) {
SetBannedSetDirty(false);
}
fprintf(stderr,"%s: Dumping banlist with %lu items\n", __func__, banmap.size());
LogPrint("net", "%s: Dumping banlist with %lu items\n", __func__, banmap.size());
LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
@@ -642,7 +652,7 @@ bool CNode::IsBanned(CNetAddr ip)
CBanEntry banEntry = (*it).second;
if(subNet.Match(ip) && GetTime() < banEntry.nBanUntil) {
fprintf(stderr,"%s: found banned subnet %s\n", __func__, subNet.ToString().c_str());
LogPrint("net", "%s: found banned subnet %s\n", __func__, subNet.ToString().c_str());
fResult = true;
}
}
@@ -676,7 +686,7 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti
if (bantimeoffset > 0)
banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;
fprintf(stderr, "%s: banning %s until %ld with bantimeoffset=%ld sinceUnixEpoch=%d\n", __func__, subNet.ToString().c_str(), banEntry.nBanUntil, bantimeoffset, sinceUnixEpoch);
LogPrint("net", "%s: banning %s until %ld with bantimeoffset=%ld sinceUnixEpoch=%d\n", __func__, subNet.ToString().c_str(), banEntry.nBanUntil, bantimeoffset, sinceUnixEpoch);
{
LOCK(cs_setBanned);
if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) {
@@ -689,14 +699,15 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (subNet.Match(static_cast<CNetAddr>(pnode->addr)))
fprintf(stderr, "%s: disconnecting from banned node %s\n", __func__, pnode->addr.ToString().c_str() );
if (subNet.Match(static_cast<CNetAddr>(pnode->addr))) {
LogPrint("net", "%s: disconnecting from banned node %s\n", __func__, pnode->addr.ToString().c_str() );
pnode->fDisconnect = true;
}
}
}
if(banReason == BanReasonManuallyAdded) {
fprintf(stderr,"%s: dumping banlist after manual ban\n", __func__);
LogPrint("net", "%s: dumping banlist after manual ban\n", __func__);
DumpBanlist(); //store banlist to disk immediately if user requested ban
}
}
@@ -815,7 +826,7 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
nPingUsecWait = GetTimeMicros() - nPingUsecStart;
}
// Raw ping time is in microseconds, but show it to user as whole seconds (Hush users should be well used to small numbers with many decimal places by now :)
// Raw ping time is in microseconds; convert to seconds for display to the user.
stats.dPingTime = (((double)nPingUsecTime) / 1e6);
stats.dMinPing = (((double)nMinPingUsecTime) / 1e6);
stats.dPingWait = (((double)nPingUsecWait) / 1e6);
@@ -1581,6 +1592,7 @@ void ThreadDNSAddressSeed()
CAddress addr = CAddress(CService(ip, ASSETCHAINS_P2PPORT));
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old
vAdd.push_back(addr);
found++;
}
}
// TODO: The seed name resolve may fail, yielding an IP of [::], which results in
@@ -1721,7 +1733,6 @@ void ThreadOpenConnections()
boost::this_thread::interruption_point();
// Add seed nodes if DNS seeds are all down (an infrastructure attack?).
// if (addrman.size() == 0 && (GetTime() - nStart > 60)) {
if (GetTime() - nStart > 60) {
static bool done = false;
if (!done) {
@@ -1851,7 +1862,6 @@ void ThreadOpenConnections()
int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000);
MilliSleep(randsleep);
LogPrint("net", "Making feeler connection to %s\n", addrConnect.ToString().c_str());
printf("%s: Making feeler connection to %s\n", __func__, addrConnect.ToString().c_str());
}
//int failures = setConnected.size() >= std::min(nMaxConnections - 1, 2);
@@ -2510,22 +2520,24 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss)
// If we have no nodes to relay to, there is nothing to do
if(vNodes.size() == 0) {
if (HUSH_TESTNODE==0) {
fprintf(stderr, "%s: No nodes to relay to!\n", __func__ );
LogPrint("net", "%s: No nodes to relay to!\n", __func__ );
}
return;
}
// We always round down, except when we have only 1 connection
// Relay to half of our peers, rounding down, but never fewer than 1.
// Equivalent to max(1, vNodes.size()/2): the ternary picks 1 only when the
// integer division vNodes.size()/2 is 0 (i.e. exactly 1 connection).
auto newSize = (vNodes.size() / 2) == 0 ? 1 : (vNodes.size() / 2);
std::shuffle( vRelayNodes.begin(), vRelayNodes.end(), std::mt19937(GetRand(std::numeric_limits<uint32_t>::max())) );
vRelayNodes.resize(newSize);
if (HUSH_TESTNODE==1 && vNodes.size() == 0) {
fprintf(stderr, "%s: -testnode=1, no peers, not relaying\n", __func__ );
LogPrint("net", "%s: -testnode=1, no peers, not relaying\n", __func__ );
return;
} else {
fprintf(stderr, "%s: Relaying %s to %lu of %lu peers\n", __func__, tx.GetHash().GetHex().c_str(), newSize, vNodes.size() );
LogPrint("net", "%s: Relaying %s to %lu of %lu peers\n", __func__, tx.GetHash().GetHex().c_str(), newSize, vNodes.size() );
}
// Only relay to randomly chosen 50% of peers
@@ -2774,7 +2786,7 @@ bool CNode::GetTlsValidate()
{
if (tlsValidate == eTlsOption::FALLBACK_UNSET)
{
// This is useful for private Hush Arrakis Chains, that want to exist
// This is useful for private DragonX-based chains that want to exist
// on a closed VPN with an internal CA or trusted cert system, or
// various other use cases
if ( GetBoolArg("-tlsvalidate", false)) {

View File

@@ -44,9 +44,12 @@
#include <boost/filesystem/path.hpp>
#include <boost/foreach.hpp>
#include <boost/signals2/signal.hpp>
// Enable WolfSSL Support for Hush
// Enable WolfSSL support for DragonX
#include <wolfssl/options.h>
// TODO: these are not set correctly by wolfssl for some reason. Ja bless.
// Force-enable wolfSSL's constant-time (timing-resistant) ECC and TFM code paths.
// These are feature-enable macros that wolfSSL checks with #ifdef, so the numeric
// value is immaterial to behavior; the value 420 is arbitrary and must simply be
// non-empty. Redefined here because wolfssl/options.h does not reliably set them.
#undef ECC_TIMING_RESISTANT
#undef TFM_TIMING_RESISTANT
#define ECC_TIMING_RESISTANT 420

View File

@@ -97,75 +97,13 @@ bnTarget = RT_CST_RST (bnTarget, ts, cw, numerator, denominator, W, T, past);
#define T ASSETCHAINS_BLOCKTIME
#define K ((int64_t)1000000)
#ifdef original_algo
arith_uint256 oldRT_CST_RST(int32_t height,uint32_t nTime,arith_uint256 bnTarget,uint32_t *ts,arith_uint256 *ct,int32_t numerator,int32_t denominator,int32_t W,int32_t past)
// The proof-of-work limit for the active algorithm: Equihash chains use params.powLimit,
// everything else (DragonX = RandomX) uses params.powAlternate. Shared by the retarget
// functions below, where this selection was previously copy-pasted as an if/else.
static arith_uint256 PowLimitForAlgo(const Consensus::Params& params)
{
//if (ts.size() < 2*W || ct.size() < 2*W ) { exit; } // error. a vector was too small
//if (ts.size() < past+W || ct.size() < past+W ) { past = min(ct.size(), ts.size()) - W; } // past was too small, adjust
int64_t altK; int32_t i,j,k,ii=0; // K is a scaling factor for integer divisions
if ( height < 64 )
return(bnTarget);
//if ( ((ts[0]-ts[W]) * W * 100)/(W-1) < (T * numerator * 100)/denominator )
if ( (ts[0] - ts[W]) < (T * numerator)/denominator )
{
//bnTarget = ((ct[0]-ct[1])/K) * max(K,(K*(nTime-ts[0])*(ts[0]-ts[W])*denominator/numerator)/T/T);
bnTarget = ct[0] / arith_uint256(K);
//altK = (K * (nTime-ts[0]) * (ts[0]-ts[W]) * denominator * W) / (numerator * (W-1) * (T * T));
altK = (K * (nTime-ts[0]) * (ts[0]-ts[W]) * denominator) / (numerator * (T * T));
fprintf(stderr,"ht.%d initial altK.%lld %d * %d * %d / %d\n",height,(long long)altK,(nTime-ts[0]),(ts[0]-ts[W]),denominator,numerator);
if ( altK > K )
altK = K;
bnTarget *= arith_uint256(altK);
if ( altK < K )
return(bnTarget);
}
/* Check past 24 blocks for any sum of 3 STs < T/2 triggers. This is messy
because the blockchain does not allow us to store a variable to know
if we are currently in a triggered state that is making a sequence of
adjustments to prevTargets, so we have to look for them.
Nested loops do this: if block emission has not slowed to be back on track at
any time since most recent trigger and we are at current block, aggressively
adust prevTarget. */
for (j=past-1; j>=2; j--)
{
if ( ts[j]-ts[j+W] < T*numerator/denominator )
{
ii = 0;
for (i=j-2; i>=0; i--)
{
ii++;
// Check if emission caught up. If yes, "trigger stopped at i".
// Break loop to try more recent j's to see if trigger activates again.
if ( (ts[i] - ts[j+W]) > (ii+W)*T )
break;
// We're here, so there was a TS[j]-TS[j-3] < T/2 trigger in the past and emission rate has not yet slowed up to be back on track so the "trigger is still active", aggressively adjusting target here at block "i"
if ( i == 0 )
{
/* We made it all the way to current block. Emission rate since
last trigger never slowed enough to get back on track, so adjust again.
If avg last 3 STs = T, this increases target to prevTarget as ST increases to T.
This biases it towards ST=~1.75*T to get emission back on track.
If avg last 3 STs = T/2, target increases to prevTarget at 2*T.
Rarely, last 3 STs can be 1/2 speed => target = prevTarget at T/2, & 1/2 at T.*/
//bnTarget = ((ct[0]-ct[W])/W/K) * (K*(nTime-ts[0])*(ts[0]-ts[W]))/W/T/T;
bnTarget = ct[0];
for (k=1; k<W; k++)
bnTarget += ct[k];
bnTarget /= arith_uint256(W * K);
altK = (K * (nTime-ts[0]) * (ts[0]-ts[W])) / (W * T * T);
fprintf(stderr,"ht.%d made it to i == 0, j.%d ii.%d altK %lld (%d * %d) %u - %u W.%d\n",height,j,ii,(long long)altK,(nTime-ts[0]),(ts[0]-ts[W]),ts[0],ts[W],W);
bnTarget *= arith_uint256(altK);
j = 0; // It needed adjusting, we adjusted it, we're finished, so break out of j loop.
}
}
}
}
return(bnTarget);
return UintToArith256(ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH ? params.powLimit : params.powAlternate);
}
#endif
arith_uint256 RT_CST_RST_outer(int32_t height,uint32_t nTime,arith_uint256 bnTarget,uint32_t *ts,arith_uint256 *ct,int32_t numerator,int32_t denominator,int32_t W,int32_t past)
{
@@ -183,13 +121,7 @@ arith_uint256 RT_CST_RST_outer(int32_t height,uint32_t nTime,arith_uint256 bnTar
}
if ( bnTarget > mintarget )
bnTarget = mintarget;
{
int32_t z;
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[z]);
}
fprintf(stderr," ht.%d initial W.%d outerK.%lld %d * %d * %d / %d\n",height,W,(long long)outerK,(nTime-ts[0]),(ts[0]-ts[W]),denominator,numerator);
} //else fprintf(stderr,"ht.%d no outer trigger %d >= %d\n",height,(ts[0] - ts[W]),(T * numerator)/denominator);
}
return(bnTarget);
}
@@ -202,13 +134,6 @@ arith_uint256 RT_CST_RST_target(int32_t height,uint32_t nTime,arith_uint256 bnTa
bnTarget /= arith_uint256(width * K);
innerK = (K * (nTime-ts[0]) * (ts[0]-ts[width])) / (width * T * T);
bnTarget *= arith_uint256(innerK);
if ( 0 )
{
int32_t z;
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[z]);
fprintf(stderr," ht.%d innerK %lld (%d * %d) %u - %u width.%d\n",height,(long long)innerK,(nTime-ts[0]),(ts[0]-ts[width]),ts[0],ts[width],width);
}
return(bnTarget);
}
@@ -223,12 +148,6 @@ arith_uint256 RT_CST_RST_inner(int32_t height,uint32_t nTime,arith_uint256 bnTar
bnTarget = RT_CST_RST_target(height,nTime,bnTarget,ts,ct,W);
if ( bnTarget == origtarget ) // force zawyflag to 1
bnTarget = mintarget;
{
int32_t z;
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[z]);
}
fprintf(stderr," height.%d O.%-2d, W.%-2d width.%-2d %4d vs %-4d, deficit %4d tip.%d\n",height,outeri,W,width,(ts[0] - ts[width]),expected,expected - (ts[0] - ts[width]),nTime-ts[0]);
}
return(bnTarget);
}
@@ -288,31 +207,19 @@ arith_uint256 zawy_TSA_EMA(int32_t height,int32_t tipdiff,arith_uint256 prevTarg
B = (bnTarget / arith_uint256(360000)) * arith_uint256(tipdiff * zawy_exponential_val360000(tipdiff/2));
C = (bnTarget / arith_uint256(360000)) * arith_uint256(T * zawy_exponential_val360000(tipdiff/2));
bnTarget = ((A + B - C) / arith_uint256(tipdiff)) * arith_uint256(K*T);
{
int32_t z;
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[z]);
}
fprintf(stderr," ht.%d TSA bnTarget tipdiff.%d\n",height,tipdiff);
return(bnTarget);
}
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
if (pindexLast->GetHeight() == 340000) {
LogPrintf("%s: Using blocktime=%d\n",__func__,ASSETCHAINS_BLOCKTIME);
}
//if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH && ASSETCHAINS_STAKED == 0)
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH && ASSETCHAINS_ALGO != ASSETCHAINS_RANDOMX) {
fprintf(stderr,"%s: using lwma for next work\n",__func__);
LogPrint("pow","%s: using lwma for next work\n",__func__);
return lwmaGetNextWorkRequired(pindexLast, pblock, params);
}
arith_uint256 bnLimit;
if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH)
bnLimit = UintToArith256(params.powLimit);
else
bnLimit = UintToArith256(params.powAlternate);
bnLimit = PowLimitForAlgo(params);
unsigned int nProofOfWorkLimit = bnLimit.GetCompact();
// Genesis block
if (pindexLast == NULL )
@@ -386,13 +293,11 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
{
blocktime = pindexFirst->nTime;
diff = (pblock->nTime - blocktime);
//fprintf(stderr,"%d ",diff);
if ( i < 6 )
{
diff -= (8+i)*ASSETCHAINS_BLOCKTIME;
if ( diff > mult )
{
//fprintf(stderr,"i.%d diff.%d (%u - %u - %dx)\n",i,(int32_t)diff,pblock->nTime,pindexFirst->nTime,(8+i));
mult = diff;
}
}
@@ -402,7 +307,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
bnTot += bnTmp;
pindexFirst = pindexFirst->pprev;
}
//fprintf(stderr,"diffs %d\n",height);
// Check we have enough blocks
if (pindexFirst == NULL)
return nProofOfWorkLimit;
@@ -499,21 +403,9 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
if ( bnTarget < origtarget || bnTarget > easy )
{
bnTarget = easy;
fprintf(stderr,"cmp.%d mult.%d ht.%d -> easy target\n",mult>1,(int32_t)mult,height);
LogPrint("pow","cmp.%d mult.%d ht.%d -> easy target\n",mult>1,(int32_t)mult,height);
return(HUSH_MINDIFF_NBITS & (~3));
}
{
int32_t z;
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[z]);
}
fprintf(stderr," exp() to the rescue cmp.%d mult.%d for ht.%d\n",mult>1,(int32_t)mult,height);
}
if ( 0 && zflags[0] == 0 && zawyflag == 0 && mult <= 1 )
{
bnTarget = zawy_TSA_EMA(height,tipdiff,(bnTarget+ct[0]+ct[1])/arith_uint256(3),ts[0] - ts[1]);
if ( bnTarget < origtarget )
zawyflag = 3;
}
}
nbits = bnTarget.GetCompact();
@@ -527,7 +419,10 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
// Changing this requires changing many other things and
// might change consensus. Have fun -- Duke
// NOTE: Ony HUSH3 mainnet should use this function, all HAC's should use params.AveragigWindowTimespan()
// NOTE: This hardcoded AWT is legacy from the original HUSH3 mainnet. On DragonX the
// CalculateNextWorkRequired strncmp(SMART_CHAIN_SYMBOL,"HUSH3",...) check is never true
// (SMART_CHAIN_SYMBOL is "DRAGONX"), so this function is dead here and the params-derived
// AveragingWindowTimespan() is used instead. Kept as-is to avoid a consensus change.
int64_t AveragingWindowTimespan() {
// used in const methods, beware!
// This is the correct AWT for 75s blocktime, before block 340k
@@ -545,8 +440,11 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
int64_t nActualTimespan = nLastBlockTime - nFirstBlockTime;
LogPrint("pow", " nActualTimespan = %d before dampening\n", nActualTimespan);
// Legacy branch: the original HUSH3 mainnet used the hardcoded AveragingWindowTimespan()
// above; every other chain uses the params-derived value. On DragonX the symbol is
// "DRAGONX", so this comparison is always false and the params value is used. The check is
// kept (rather than removed) because it is part of consensus difficulty calculation.
bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
// If this is HUSH3, use AWT function defined above, else use the one in params
int64_t AWT = ishush3 ? AveragingWindowTimespan() : params.AveragingWindowTimespan();
nActualTimespan = AWT + (nActualTimespan - AWT)/4;
@@ -568,10 +466,7 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
}
// Retarget
arith_uint256 bnLimit;
if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH)
bnLimit = UintToArith256(params.powLimit);
else
bnLimit = UintToArith256(params.powAlternate);
bnLimit = PowLimitForAlgo(params);
const arith_uint256 bnPowLimit = bnLimit; //UintToArith256(params.powLimit);
arith_uint256 bnNew {bnAvg};
@@ -594,8 +489,9 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
return bnNew.GetCompact();
}
// HUSH does not use these functions but Hush Arrakis Chains can opt-in to using more bleeding edge DAA's
// ASIC chains do not need these protections as much -- Duke Leto
// These LWMA difficulty functions are inherited from the Hush lineage and are only used when
// ASSETCHAINS_ALGO is neither Equihash nor RandomX (see the dispatch in GetNextWorkRequired).
// DragonX uses RandomX, so this LWMA path is not on DragonX's active difficulty codepath.
unsigned int lwmaGetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
return lwmaCalculateNextWorkRequired(pindexLast, params);
@@ -604,14 +500,10 @@ unsigned int lwmaGetNextWorkRequired(const CBlockIndex* pindexLast, const CBlock
unsigned int lwmaCalculateNextWorkRequired(const CBlockIndex* pindexLast, const Consensus::Params& params)
{
arith_uint256 nextTarget {0}, sumTarget {0}, bnTmp, bnLimit;
if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH)
bnLimit = UintToArith256(params.powLimit);
else
bnLimit = UintToArith256(params.powAlternate);
bnLimit = PowLimitForAlgo(params);
unsigned int nProofOfWorkLimit = bnLimit.GetCompact();
//printf("PoWLimit: %u\n", nProofOfWorkLimit);
// Find the first block in the averaging interval as we total the linearly weighted average
const CBlockIndex* pindexFirst = pindexLast;
const CBlockIndex* pindexNext;
@@ -872,14 +764,6 @@ bool CheckRandomXSolution(const CBlockHeader *pblock, int32_t height)
snprintf(buf, sizeof(buf), "%02x", pblock->nSolution[i]);
solutionHex += buf;
}
fprintf(stderr, "CheckRandomXSolution(): HASH MISMATCH at height %d\n", height);
fprintf(stderr, " computed : %s\n", computedHex.c_str());
fprintf(stderr, " nSolution: %s\n", solutionHex.c_str());
fprintf(stderr, " rxKey size=%lu, input size=%lu, nNonce=%s\n",
rxKey.size(), ssInput.size(), pblock->nNonce.ToString().c_str());
fprintf(stderr, " nSolution.size()=%lu, RANDOMX_HASH_SIZE=%d\n",
pblock->nSolution.size(), RANDOMX_HASH_SIZE);
// Also log to debug.log
LogPrintf("CheckRandomXSolution(): HASH MISMATCH at height %d\n", height);
LogPrintf(" computed : %s\n", computedHex);
LogPrintf(" nSolution: %s\n", solutionHex);

View File

@@ -42,6 +42,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getaddednodeinfo", 0 },
{ "setgenerate", 0 },
{ "setgenerate", 1 },
{ "stratummine", 1 }, // port
{ "stratummine", 3 }, // timeout
{ "generate", 0 },
{ "getnetworkhashps", 0 },
{ "getnetworkhashps", 1 },
@@ -50,8 +52,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendtoaddress", 1 },
{ "sendtoaddress", 4 },
{ "settxfee", 0 },
{ "getnotarysendmany", 0 },
{ "getnotarysendmany", 1 },
{ "getreceivedbyaddress", 1 },
{ "getreceivedbyaccount", 1 },
{ "listreceivedbyaddress", 0 },
@@ -171,7 +171,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
// crosschain
{ "assetchainproof", 1},
{ "crosschainproof", 1},
{ "getproofroot", 2},
{ "getNotarizationsForBlock", 0},
{ "height_MoM", 1},

View File

@@ -78,14 +78,6 @@ UniValue assetchainproof(const UniValue& params, bool fHelp, const CPubKey& mypk
}
UniValue crosschainproof(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
UniValue ret(UniValue::VOBJ);
//fprintf(stderr,"crosschainproof needs to be implemented\n");
return(ret);
}
UniValue height_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
int32_t height,depth,notarized_height,MoMoMdepth,MoMoMoffset,hushstarti,hushendi; uint256 MoM,MoMoM,hushtxid; uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR);

View File

@@ -45,6 +45,21 @@
#include <univalue.h>
#include "compat/byteswap.h" // bswap_32 for the stratum wire fields (version/time/bits)
#ifndef WIN32
// stratummine (below) is a POSIX-only reference RandomX stratum miner used to exercise the pool
// path end-to-end. It reuses DragonX's own RandomX + GetRandomXInput so its hash is byte-identical
// to CheckRandomXSolution. Not built on Windows (raw POSIX sockets).
#include "RandomX/src/randomx.h"
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#endif
using namespace std;
#include "hush_defs.h"
@@ -363,7 +378,7 @@ UniValue setgenerate(const UniValue& params, bool fHelp, const CPubKey& mypk)
}
HUSH_MININGTHREADS = (int32_t)nGenProcLimit;
fprintf(stderr,"%s:HUSH_MININGTHREADS=%d\n", __FUNCTION__, HUSH_MININGTHREADS);
LogPrint("mining","%s:HUSH_MININGTHREADS=%d\n", __FUNCTION__, HUSH_MININGTHREADS);
mapArgs["-gen"] = (fGenerate ? "1" : "0");
mapArgs ["-genproclimit"] = itostr(HUSH_MININGTHREADS);
@@ -460,15 +475,8 @@ UniValue getmininginfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty()));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1)));
if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH)
{
obj.push_back(Pair("localsolps" , getlocalsolps(params, false, mypk)));
obj.push_back(Pair("networksolps", getnetworksolps(params, false, mypk)));
}
else
{
obj.push_back(Pair("localhashps" , GetBoolArg("-gen", false) ? getlocalsolps(params, false, mypk) : (double)0.0));
}
// DragonX is RandomX-only; the Equihash sol/s reporting path was removed.
obj.push_back(Pair("localhashps" , GetBoolArg("-gen", false) ? getlocalsolps(params, false, mypk) : (double)0.0));
obj.push_back(Pair("networkhashps", getnetworksolps(params, false, mypk)));
obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
@@ -854,7 +862,6 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp, const CPubKey& myp
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
result.push_back(Pair("height", (int64_t)(pindexPrev->GetHeight()+1)));
//fprintf(stderr,"return complete template\n");
return result;
}
@@ -905,7 +912,6 @@ UniValue submitblock(const UniValue& params, bool fHelp, const CPubKey& mypk)
);
CBlock block;
//LogPrintStr("Hex block submission: " + params[0].get_str());
if (!DecodeHexBlk(block, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
@@ -931,7 +937,6 @@ UniValue submitblock(const UniValue& params, bool fHelp, const CPubKey& mypk)
CValidationState state;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
//printf("submitblock, height=%d, coinbase sequence: %d, scriptSig: %s\n", chainActive.LastTip()->GetHeight()+1, block.vtx[0].vin[0].nSequence, block.vtx[0].vin[0].scriptSig.ToString().c_str());
bool fAccepted = ProcessNewBlock(1,chainActive.LastTip()->GetHeight()+1,state, NULL, &block, true, NULL);
UnregisterValidationInterface(&sc);
if (fBlockPresent)
@@ -1063,9 +1068,242 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp, const CPubKey& mypk
}
#ifndef WIN32
extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_RANDOMX; // hush_defs.h — active PoW algorithm
// Send one newline-terminated JSON line on a blocking socket.
static bool StratumMinerSend(int fd, const std::string& s)
{
std::string line = s;
if (line.empty() || line.back() != '\n') line += '\n';
size_t off = 0;
while (off < line.size()) {
ssize_t n = send(fd, line.data() + off, line.size() - off, 0);
if (n <= 0) return false;
off += (size_t)n;
}
return true;
}
// Wait up to timeout_ms for data, then split all completed lines out of buf into out.
// Returns false only on socket error/close (a timeout with no data is success with out empty).
static bool StratumMinerRecvLines(int fd, std::string& buf, int timeout_ms, std::vector<std::string>& out)
{
fd_set rfds; FD_ZERO(&rfds); FD_SET(fd, &rfds);
struct timeval tv; tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000;
int r = select(fd + 1, &rfds, NULL, NULL, &tv);
if (r < 0) return false;
if (r == 0) return true;
char tmp[8192];
ssize_t n = recv(fd, tmp, sizeof(tmp), 0);
if (n <= 0) return false;
buf.append(tmp, tmp + n);
size_t pos;
while ((pos = buf.find('\n')) != std::string::npos) {
std::string line = buf.substr(0, pos);
buf.erase(0, pos + 1);
if (!line.empty() && line.back() == '\r') line.pop_back();
if (!line.empty()) out.push_back(line);
}
return true;
}
// Reference RandomX stratum miner (test utility): connect to a DragonX stratum server, subscribe +
// authorize, receive work + the per-height RandomX key, then vary the block nNonce, hash with
// RandomX (byte-identical to CheckRandomXSolution via GetRandomXInput), and submit a 32-byte
// solution when the block hash meets target. Exists to validate the -stratum RandomX pool path.
UniValue stratummine(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() < 2 || params.size() > 4)
throw runtime_error(
"stratummine \"host\" port ( \"address\" timeout )\n"
"\nReference RandomX stratum miner: connect to a DragonX stratum server, solve RandomX,\n"
"and submit until one share/block is accepted or the timeout elapses. For testing -stratum.\n"
"\nArguments:\n"
"1. \"host\" (string, required) stratum server host or IP\n"
"2. port (numeric, required) stratum server port\n"
"3. \"address\" (string, optional, default=\"x\") payout R-address, or \"x\" for the server default\n"
"4. timeout (numeric, optional, default=120) seconds to mine before giving up\n"
"\nResult: {\"found\":bool,\"accepted\":bool,\"hash\":\"..\",\"hashes\":n,\"seconds\":n}\n");
if (ASSETCHAINS_ALGO != ASSETCHAINS_RANDOMX)
throw JSONRPCError(RPC_MISC_ERROR, "stratummine only supports RandomX chains");
const std::string host = params[0].get_str();
const int port = params[1].get_int();
const std::string addr = params.size() > 2 ? params[2].get_str() : "x";
const int64_t timeout = params.size() > 3 ? params[3].get_int64() : 120;
const int64_t deadline = GetTime() + timeout;
// connect (blocking TCP)
struct addrinfo hints; memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM;
struct addrinfo* ai = NULL;
if (getaddrinfo(host.c_str(), strprintf("%d", port).c_str(), &hints, &ai) != 0 || !ai)
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, strprintf("cannot resolve %s:%d", host, port));
int fd = -1;
for (struct addrinfo* p = ai; p; p = p->ai_next) {
fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (fd < 0) continue;
if (connect(fd, p->ai_addr, p->ai_addrlen) == 0) break;
close(fd); fd = -1;
}
freeaddrinfo(ai);
if (fd < 0)
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, strprintf("cannot connect to %s:%d", host, port));
{ int one = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)); }
StratumMinerSend(fd, "{\"id\":1,\"method\":\"mining.subscribe\",\"params\":[\"dragonx-refminer/1.0\"]}");
StratumMinerSend(fd, strprintf("{\"id\":2,\"method\":\"mining.authorize\",\"params\":[\"%s\",\"x\"]}", addr));
// state accumulated from the server
std::vector<unsigned char> extranonce1;
std::string rxKey;
bool haveKey = false, haveTarget = false, haveJob = false;
arith_uint256 poolTarget;
std::string jobId, timeHex;
uint32_t nVersion = 4, nTime = 0, nBits = 0;
uint256 hashPrevBlock, hashMerkleRoot, hashReserved;
auto processLine = [&](const std::string& line) {
UniValue v;
if (!v.read(line)) return;
const UniValue& id = find_value(v, "id");
const UniValue& result = find_value(v, "result");
if (id.isNum() && id.get_int() == 1 && result.isArray() && result.size() >= 2 && result[1].isStr())
extranonce1 = ParseHex(result[1].get_str());
const UniValue& method = find_value(v, "method");
if (!method.isStr()) return;
const UniValue& p = find_value(v, "params");
if (!p.isArray()) return;
const std::string m = method.get_str();
if (m == "mining.set_randomx_key" && p.size() >= 1) {
std::vector<unsigned char> kb = ParseHex(p[0].get_str());
rxKey.assign(kb.begin(), kb.end());
haveKey = true;
} else if (m == "mining.set_target" && p.size() >= 1) {
poolTarget = UintToArith256(uint256S(p[0].get_str()));
haveTarget = true;
} else if (m == "mining.notify" && p.size() >= 7) {
jobId = p[0].get_str();
nVersion = bswap_32((uint32_t)strtoul(p[1].get_str().c_str(), NULL, 16));
hashPrevBlock = uint256(ParseHex(p[2].get_str()));
hashMerkleRoot = uint256(ParseHex(p[3].get_str()));
hashReserved = uint256(ParseHex(p[4].get_str()));
timeHex = p[5].get_str();
nTime = bswap_32((uint32_t)strtoul(timeHex.c_str(), NULL, 16));
nBits = bswap_32((uint32_t)strtoul(p[6].get_str().c_str(), NULL, 16));
haveJob = true;
}
};
std::string buf;
for (int i = 0; i < 120 && !(haveJob && haveTarget && haveKey && !extranonce1.empty()); i++) {
std::vector<std::string> lines;
if (!StratumMinerRecvLines(fd, buf, 250, lines)) { close(fd); throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "stratum connection closed during handshake"); }
for (const std::string& l : lines) processLine(l);
if (GetTime() > deadline) break;
}
if (!(haveJob && haveTarget && haveKey && !extranonce1.empty())) {
close(fd);
throw JSONRPCError(RPC_MISC_ERROR, "did not receive complete RandomX work (need job + target + randomx key + extranonce)");
}
randomx_flags flags = randomx_get_flags();
randomx_cache* cache = randomx_alloc_cache(flags);
if (!cache) { close(fd); throw JSONRPCError(RPC_MISC_ERROR, "randomx_alloc_cache failed"); }
randomx_init_cache(cache, rxKey.data(), rxKey.size());
std::string vmKey = rxKey;
randomx_vm* vm = randomx_create_vm(flags, cache, NULL);
if (!vm) { randomx_release_cache(cache); close(fd); throw JSONRPCError(RPC_MISC_ERROR, "randomx_create_vm failed"); }
UniValue res(UniValue::VOBJ);
bool found = false, accepted = false, submitted = false;
uint64_t hashes = 0, en2ctr = 0;
std::string foundHash;
const int64_t started = GetTime();
while (GetTime() <= deadline && !found) {
std::string prevJob = jobId;
std::vector<std::string> lines;
if (!StratumMinerRecvLines(fd, buf, 0, lines)) break;
for (const std::string& l : lines) processLine(l);
if (jobId != prevJob) en2ctr = 0; // new tip -> restart the nonce search
if (rxKey != vmKey) { randomx_init_cache(cache, rxKey.data(), rxKey.size()); randomx_vm_set_cache(vm, cache); vmKey = rxKey; }
arith_uint256 blockTarget; bool fNeg, fOver;
blockTarget.SetCompact(nBits, &fNeg, &fOver);
// Mine to the harder of (block target, pool share target) so a solution is a real block AND
// passes the server's low-diff share check.
arith_uint256 tgt = (haveTarget && poolTarget < blockTarget) ? poolTarget : blockTarget;
CBlockHeader hdr;
hdr.nVersion = nVersion;
hdr.hashPrevBlock = hashPrevBlock;
hdr.hashMerkleRoot = hashMerkleRoot;
hdr.hashFinalSaplingRoot = hashReserved;
hdr.nTime = nTime;
hdr.nBits = nBits;
for (int i = 0; i < 2000 && GetTime() <= deadline; i++) {
std::vector<unsigned char> nonce = extranonce1;
nonce.resize(32, 0);
for (int b = 0; b < 8; b++) nonce[8 + b] = (unsigned char)((en2ctr >> (8 * b)) & 0xff);
en2ctr++; hashes++;
hdr.nNonce = uint256(nonce);
std::vector<unsigned char> input = GetRandomXInput(hdr);
unsigned char h[RANDOMX_HASH_SIZE];
randomx_calculate_hash(vm, input.data(), input.size(), h);
hdr.nSolution.assign(h, h + RANDOMX_HASH_SIZE);
if (UintToArith256(hdr.GetHash()) <= tgt) {
std::vector<unsigned char> en2(nonce.begin() + 8, nonce.end());
std::string submit = strprintf(
"{\"id\":4,\"method\":\"mining.submit\",\"params\":[\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"]}",
addr, jobId, timeHex, HexStr(en2), HexStr(hdr.nSolution));
StratumMinerSend(fd, submit);
submitted = true;
foundHash = hdr.GetHash().ToString();
bool sawResult = false;
for (int k = 0; k < 40 && !sawResult; k++) {
std::vector<std::string> rl;
if (!StratumMinerRecvLines(fd, buf, 250, rl)) break;
for (const std::string& l : rl) {
processLine(l);
UniValue rv; if (!rv.read(l)) continue;
const UniValue& rid = find_value(rv, "id");
if (rid.isNum() && rid.get_int() == 4) {
sawResult = true;
const UniValue& r = find_value(rv, "result");
accepted = r.isBool() ? r.get_bool() : find_value(rv, "error").isNull();
}
}
}
found = true;
break;
}
}
}
randomx_destroy_vm(vm);
randomx_release_cache(cache);
close(fd);
res.push_back(Pair("found", found));
res.push_back(Pair("submitted", submitted));
res.push_back(Pair("accepted", accepted));
res.push_back(Pair("hashes", (uint64_t)hashes));
res.push_back(Pair("seconds", (int64_t)(GetTime() - started)));
if (!foundHash.empty()) res.push_back(Pair("hash", foundHash));
return res;
}
#endif // !WIN32
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
// --------------------- ------------------------ ----------------------- ----------
#ifndef WIN32
{ "mining", "stratummine", &stratummine, true },
#endif
{ "mining", "getlocalsolps", &getlocalsolps, true },
{ "mining", "getnetworksolps", &getnetworksolps, true },
{ "mining", "getnetworkhashps", &getnetworkhashps, true },

View File

@@ -78,106 +78,6 @@ extern int32_t ASSETCHAINS_SAPLING;
extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[],ASSETCHAINS_NOTARY_PAY[];
extern std::string NOTARY_PUBKEY,NOTARY_ADDRESS; extern uint8_t NOTARY_PUBKEY33[];
//TODO: use non-staked eras
// Currently HUSH only uses block heights to define eras
int32_t getera(int timestamp)
{
return(0);
}
UniValue getdragonjson(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 0)
throw runtime_error("getdragonjson\nreturns json for dragon, for the current ERA.");
UniValue json(UniValue::VOBJ);
UniValue seeds(UniValue::VARR);
UniValue notaries(UniValue::VARR);
// get the current era, use local time for now.
// should ideally take blocktime of last known block?
int now = time(NULL);
int32_t era = getera(now);
// loop over seeds array and push back to json array for seeds
for (int8_t i = 0; i < 8; i++) {
//seeds.push_back(dragonSeeds[i][0]);
}
// get all current notaries
for (int8_t i = 0; i < NUM_HUSH_NOTARIES; i++) {
UniValue notary(UniValue::VOBJ);
notary.push_back(notaries_list[era][i][0]);
notaries.push_back(notary);
}
// TODO: should be a config param
int minsigs = 13;
int BTCminsigs = 13;
int dragonPort = 5555;
json.push_back(Pair("port",dragonPort));
json.push_back(Pair("BTCminsigs",BTCminsigs));
json.push_back(Pair("minsigs",minsigs));
json.push_back(Pair("seeds",seeds));
json.push_back(Pair("notaries",notaries));
return json;
}
UniValue getnotarysendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"getnotarysendmany\n"
"Returns a sendmany JSON array with all current notaries Raddress's.\n"
"\nExamples:\n"
+ HelpExampleCli("getnotarysendmany", "10")
+ HelpExampleRpc("getnotarysendmany", "10")
);
int amount = 0;
if ( params.size() == 1 ) {
amount = params[0].get_int();
}
//TODO: this is broke
int era = getera(time(NULL));
UniValue ret(UniValue::VOBJ);
for (int i = 0; i<NUM_HUSH_NOTARIES; i++)
{
char Raddress[18]; uint8_t pubkey33[33];
decode_hex(pubkey33,33,(char *)notaries_list[era][i][1]);
pubkey2addr((char *)Raddress,(uint8_t *)pubkey33);
ret.push_back(Pair(Raddress,amount));
}
return ret;
}
UniValue geterablockheights(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"geterablockheights\n"
"Returns a JSON object with the first block in each era.\n"
);
CBlockIndex *pindex; int8_t lastera,era = 0; UniValue ret(UniValue::VOBJ);
for (size_t i = 1; i < chainActive.LastTip()->GetHeight(); i++)
{
pindex = chainActive[i];
era = getera(pindex->nTime)+1;
if ( era > lastera )
{
char str[16];
sprintf(str, "%d", era);
ret.push_back(Pair(str,(int64_t)i));
lastera = era;
}
}
return(ret);
}
extern int getWorkQueueDepth();
extern int getWorkQueueMaxDepth();
extern int getWorkQueueNumThreads();
@@ -202,7 +102,7 @@ UniValue rpcinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height,longestchain,hushnotarized_height,txid_height;
int32_t longestchain;
if (fHelp || params.size() != 0)
throw runtime_error(
"getinfo\n"
@@ -240,28 +140,13 @@ UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
proxyType proxy;
GetProxy(NET_IPV4, proxy);
notarized_height = hush_notarized_height(&prevMoMheight,&notarized_hash,&notarized_desttxid);
//fprintf(stderr,"after notarized_height %u\n",(uint32_t)time(NULL));
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("version", CLIENT_VERSION));
obj.push_back(Pair("protocolversion", PROTOCOL_VERSION));
obj.push_back(Pair("synced", HUSH_INSYNC!=0));
obj.push_back(Pair("notarized", notarized_height));
obj.push_back(Pair("prevMoMheight", prevMoMheight));
obj.push_back(Pair("notarizedhash", notarized_hash.ToString()));
obj.push_back(Pair("notarizedtxid", notarized_desttxid.ToString()));
if ( HUSH_NSPV_FULLNODE )
{
txid_height = notarizedtxid_height( (char *)"HUSH3" ,(char *)notarized_desttxid.ToString().c_str(),&hushnotarized_height);
if ( txid_height > 0 )
obj.push_back(Pair("notarizedtxid_height", txid_height));
else obj.push_back(Pair("notarizedtxid_height", "mempool"));
if ( SMART_CHAIN_SYMBOL[0] != 0 ) {
obj.push_back(Pair("HUSHnotarized_height", hushnotarized_height));
}
obj.push_back(Pair("notarized_confirms", txid_height < hushnotarized_height ? (hushnotarized_height - txid_height + 1) : 0));
//fprintf(stderr,"after notarized_confirms %u\n",(uint32_t)time(NULL));
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
@@ -348,14 +233,8 @@ UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
if ( ASSETCHAINS_COMMISSION != 0 )
obj.push_back(Pair("commission", ASSETCHAINS_COMMISSION));
if ( ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH ) {
uint64_t N = ASSETCHAINS_NK[0] ? ASSETCHAINS_NK[0] : 200;
uint64_t K = ASSETCHAINS_NK[1] ? ASSETCHAINS_NK[1] : 9;
std::string equihash_algo = "equihash (" + std::to_string(N) + "," + std::to_string(K) + ")";
obj.push_back(Pair("algo",equihash_algo));
} else {
obj.push_back(Pair("algo", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]));
}
// DragonX is RandomX-only; the Equihash (N,K) reporting path was removed.
obj.push_back(Pair("algo", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]));
}
return obj;
}

View File

@@ -218,7 +218,7 @@ int32_t HUSH_LONGESTCHAIN;
static int32_t hush_longest_depth = 0;
int32_t hush_longestchain()
{
int32_t ht,n=0,num=0,maxheight=0,height = 0;
int32_t ht,num=0,maxheight=0,height = 0;
if ( hush_longest_depth < 0 )
hush_longest_depth = 0;
if ( hush_longest_depth == 0 )
@@ -231,7 +231,6 @@ int32_t hush_longestchain()
}
BOOST_FOREACH(const CNodeStats& stats, vstats)
{
//fprintf(stderr,"hush_longestchain iter.%d\n",n);
CNodeStateStats statestats;
bool fStateStats = GetNodeStateStats(stats.nodeid,statestats);
if ( statestats.nSyncHeight < 0 )
@@ -251,10 +250,8 @@ int32_t hush_longestchain()
height = ht;
}
hush_longest_depth--;
if ( num > (n >> 1) )
if ( num > 0 )
{
if ( 0 && height != HUSH_LONGESTCHAIN )
fprintf(stderr,"set %s HUSH_LONGESTCHAIN <- %d\n",SMART_CHAIN_SYMBOL,height);
HUSH_LONGESTCHAIN = height;
return(height);
}

View File

@@ -1168,7 +1168,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp, const CPubKey& m
numiters++;
}
if ( numiters > 0 )
fprintf(stderr,"ASSETCHAINS_TXPOW.%d txpow.%d numiters.%d for signature\n",ASSETCHAINS_TXPOW,txpow,numiters);
LogPrintf("ASSETCHAINS_TXPOW.%d txpow.%d numiters.%d for signature\n",ASSETCHAINS_TXPOW,txpow,numiters);
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);

View File

@@ -276,11 +276,7 @@ UniValue stop(const UniValue& params, bool fHelp, const CPubKey& mypk)
// Shutdown will take long enough that the response should get back
StartShutdown();
if ((strncmp(SMART_CHAIN_SYMBOL, "HUSH3", 5) == 0) ) {
sprintf(buf,"Hush server stopping, for now...");
} else {
sprintf(buf,"%s server stopping...",SMART_CHAIN_SYMBOL);
}
sprintf(buf,"%s server stopping...",SMART_CHAIN_SYMBOL);
return buf;
}
@@ -292,9 +288,6 @@ static const CRPCCommand vRPCCommands[] =
// --------------------- ------------------------ ----------------------- ----------
/* Overall control/query calls */
{ "control", "help", &help, true },
{ "control", "getdragonjson", &getdragonjson, true },
{ "control", "getnotarysendmany", &getnotarysendmany, true },
{ "control", "geterablockheights", &geterablockheights, true },
{ "control", "stop", &stop, true },
/* P2P networking */
@@ -342,7 +335,6 @@ static const CRPCCommand vRPCCommands[] =
{ "crosschain", "calc_MoM", &calc_MoM, true },
{ "crosschain", "height_MoM", &height_MoM, true },
{ "crosschain", "assetchainproof", &assetchainproof, true },
{ "crosschain", "crosschainproof", &crosschainproof, true },
{ "crosschain", "getNotarizationsForBlock", &getNotarizationsForBlock, true },
{ "crosschain", "scanNotarizationsDB", &scanNotarizationsDB, true },
@@ -666,7 +658,6 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue &params
// while a very long wallet rescan is happening and do other read-only devopz
if (pcmd->name != "stop" && pcmd->name != "help" && pcmd->name != "z_listaddresses" && pcmd->name != "z_exportkey" &&
pcmd->name != "getNotarizationsForBlock" && pcmd->name != "scanNotarizationsDB" &&
pcmd->name != "getnotarysendmany" && pcmd->name != "geterablockheights" &&
pcmd->name != "getaddressesbyaccount" && pcmd->name != "listaddresses" && pcmd->name != "z_exportwallet" &&
pcmd->name != "notaries" && pcmd->name != "signmessage" && pcmd->name != "decoderawtransaction" &&
pcmd->name != "dumpprivkey" && pcmd->name != "getpeerinfo" && pcmd->name != "getnetworkinfo" &&
@@ -695,11 +686,7 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue &params
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
{
if ((strncmp(SMART_CHAIN_SYMBOL, "HUSH3", 5) == 0) ) {
return "> hush-cli " + methodname + " " + args + "\n";
} else {
return "> hush-cli -ac_name=" + strprintf("%s", SMART_CHAIN_SYMBOL) + " " + methodname + " " + args + "\n";
}
return "> dragonx-cli " + methodname + " " + args + "\n";
}
std::string HelpExampleRpc(const std::string& methodname, const std::string& args)

View File

@@ -280,9 +280,6 @@ extern UniValue encryptwallet(const UniValue& params, bool fHelp, const CPubKey&
extern UniValue validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue txnotarizedconfirmed(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue getdragonjson(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue getnotarysendmany(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue geterablockheights(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue setpubkey(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue getwalletinfo(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue getblockchaininfo(const UniValue& params, bool fHelp, const CPubKey& mypk);
@@ -383,7 +380,6 @@ extern UniValue MoMoMdata(const UniValue& params, bool fHelp, const CPubKey& myp
extern UniValue calc_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue height_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue assetchainproof(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue crosschainproof(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue getNotarizationsForBlock(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue scanNotarizationsDB(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue getimports(const UniValue& params, bool fHelp, const CPubKey& mypk);

View File

@@ -16,6 +16,7 @@
#include "httpserver.h"
#include "miner.h"
#include "netbase.h"
#include "pow.h" // RandomX PoW: CheckRandomXSolution / GetRandomXKey / GetRandomXInput; and CheckEquihashSolution
#include "net.h"
#include "rpc/server.h"
#include "serialize.h"
@@ -637,9 +638,6 @@ void CustomizeWork(const StratumClient& client, const StratumWork& current_work,
nonce.insert(nonce.end(), extranonce2.begin(), extranonce2.end());
// nonce = extranonce1 + extranonce2
// if (instance_of_cstratumparams.fstdErrDebugOutput) {
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " nonce = " << HexStr(nonce) << std::endl;
// }
if (cb.vin.empty()) {
const std::string msg = strprintf("%s: first transaction is missing coinbase input; unable to customize work to miner", __func__);
@@ -658,14 +656,32 @@ void CustomizeWork(const StratumClient& client, const StratumWork& current_work,
LogPrint("stratum", "%s\n", msg);
throw std::runtime_error(msg);
}
if (cb.vout[0].scriptPubKey == (CScript() << OP_FALSE)) {
cb.vout[0].scriptPubKey = GetScriptForDestination(addr.Get());
// Unconditional. This used to be guarded on the coinbase still carrying the OP_FALSE
// placeholder, which made it a no-op for every client after the first once a customized
// coinbase had been written back into the shared template -- so those miners silently
// mined the first miner's payout address. The template is now left pristine (see
// GetWorkUnit), and stamping unconditionally means a coinbase that somehow arrives
// already customized can never be inherited by a different miner.
if (!addr.IsValid()) {
const std::string msg = strprintf("%s: no valid payout address for this client; unable to customize work", __func__);
LogPrint("stratum", "%s\n", msg);
throw std::runtime_error(msg);
}
cb.vout[0].scriptPubKey = GetScriptForDestination(addr.Get());
}
// cb_branch = current_work.m_cb_branch;
}
// DragonX PoW is RandomX (32-byte solution); Equihash is legacy (1347-byte solution). The stratum
// work and submit paths branch on this: RandomX hands the miner the per-height RandomX key (which it
// cannot derive without the chain) and validates a 32-byte solution via CheckRandomXSolution();
// Equihash keeps the legacy path (1347-byte solution + the 3-byte prefix + CheckEquihashSolution).
extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_RANDOMX; // hush_defs.h — active PoW algorithm selector
extern int32_t HUSH_TESTNODE; // hush_globals.h — -testnode: relax IBD/sync guards for isolated test nodes
static inline bool StratumIsRandomX() { return ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX; }
static const size_t RX_STRATUM_SOLUTION_SIZE = 32; // == RANDOMX_HASH_SIZE (kept local to avoid pulling randomx.h into stratum)
std::string GetWorkUnit(StratumClient& client)
{
// LOCK(cs_main);
@@ -675,7 +691,7 @@ std::string GetWorkUnit(StratumClient& client)
} */
/* if (!Params().MineBlocksOnDemand() && g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Hush is not connected!");
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "DragonX is not connected!");
} */
bool fvNodesEmpty;
@@ -686,21 +702,21 @@ std::string GetWorkUnit(StratumClient& client)
if (Params().MiningRequiresPeers() && fvNodesEmpty)
{
const std::string msg = strprintf("%s: Unable to get work unit, Hush is not connected!", __func__);
const std::string msg = strprintf("%s: Unable to get work unit, DragonX is not connected!", __func__);
LogPrint("stratum", "%s\n", msg);
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Hush is not connected!");
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "DragonX is not connected!");
}
if (IsInitialBlockDownload()) {
const std::string msg = strprintf("%s: Unable to get work unit, Hush is still downloading blocks!", __func__);
if (IsInitialBlockDownload() && HUSH_TESTNODE == 0) {
const std::string msg = strprintf("%s: Unable to get work unit, DragonX is still downloading blocks!", __func__);
LogPrint("stratum", "%s\n", msg);
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Hush is downloading blocks...");
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "DragonX is downloading blocks...");
}
if (!client.m_authorized && client.m_aux_addr.empty()) {
const std::string msg = strprintf("%s: Unable to get work unit, client not authorized! Use address 'x' to mine to the default address", __func__);
LogPrint("stratum", "%s\n", msg);
throw JSONRPCError(RPC_INVALID_REQUEST, "Stratum client not authorized. Use mining.authorize first, with a Hush R.. address as the username or 'x' to mine to the default address.");
throw JSONRPCError(RPC_INVALID_REQUEST, "Stratum client not authorized. Use mining.authorize first, with a DragonX R.. address as the username or 'x' to mine to the default address.");
}
static CBlockIndex* tip = NULL; // pindexPrev
@@ -737,18 +753,18 @@ std::string GetWorkUnit(StratumClient& client)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
}
// if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << "hashMerkleRoot = " << new_work->block.hashMerkleRoot.ToString() << std::endl;
// So that block.GetHash() is correct
//new_work->block.hashMerkleRoot = BlockMerkleRoot(new_work->block);
new_work->block.hashMerkleRoot = new_work->block.BuildMerkleTree();
// NB! here we have merkle with scriptDummy script in coinbase, after CustomizeWork we should recalculate it (!)
// if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << "hashMerkleRoot = " << new_work->block.hashMerkleRoot.ToString() << std::endl;
job_id = new_work->block.GetHash();
//work_templates[job_id] = StratumWork(*new_work, new_work->block.vtx[0]->HasWitness());
work_templates[job_id] = StratumWork(*new_work, false);
// Height of the block being mined — used for RandomX key derivation (GetRandomXKey) and
// CheckRandomXSolution/CheckProofOfWork on submit. Previously left 0 (Equihash didn't need it).
work_templates[job_id].nHeight = tip_new->GetHeight() + 1;
tip = tip_new;
@@ -851,38 +867,30 @@ std::string GetWorkUnit(StratumClient& client)
CMutableTransaction cb, bf;
std::vector<uint256> cb_branch;
// if (instance_of_cstratumparams.fstdErrDebugOutput)
// {
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " [1] cb = " << CTransaction(cb).ToString() << std::endl;
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " [1] current_work.GetBlock().vtx[0] = " << current_work.GetBlock().vtx[0].ToString() << std::endl;
// }
{
std::vector<unsigned char> extranonce1 = client.ExtraNonce1(job_id);
static const std::vector<unsigned char> dummy(32-extranonce1.size(), 0x00); // extranonce2
CustomizeWork(client, current_work, client.m_addr, extranonce1, dummy, cb, bf, cb_branch);
// without 2 lines below equihash solutinon on SubmitWork will be incorrect, bcz we should
// change vtx[0] in current work and re-calc hashMerkleRoot
// TODO: refactor all of these ... may be change this in current_work directly is bad idea,
// and we should do all checks and hashMerkleRoot at SubmitBlock(...)
current_work.GetBlock().vtx[0] = cb;
current_work.GetBlock().hashMerkleRoot = current_work.GetBlock().BuildMerkleTree();
}
// if (instance_of_cstratumparams.fstdErrDebugOutput)
// {
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " [2] cb = " << CTransaction(cb).ToString() << std::endl;
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " [2] current_work.GetBlock().vtx[0] = " << current_work.GetBlock().vtx[0].ToString() << std::endl;
// }
CBlockHeader blkhdr;
// Setup native proof-of-work
blkhdr = current_work.GetBlock().GetBlockHeader(); // copy entire blockheader created with CreateNewBlock to blkhdr
// The shared template MUST keep its pristine OP_FALSE coinbase. This previously did
// current_work.GetBlock().vtx[0] = cb;
// current_work.GetBlock().hashMerkleRoot = current_work.GetBlock().BuildMerkleTree();
// which published one client's coinbase to every other client on the same job: the merkle
// root they were told to mine, and the block they eventually submitted, both committed to
// the first client's payout address. Derive this client's header from a local copy instead,
// which is what the TODO that used to sit here was asking for.
{
CBlock tmp(current_work.GetBlock());
tmp.vtx[0] = cb;
blkhdr = tmp.GetBlockHeader();
blkhdr.hashMerkleRoot = tmp.BuildMerkleTree();
}
// CDataStream ds(SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
CDataStream ds(SER_GETHASH, PROTOCOL_VERSION);
ds << cb;
@@ -934,7 +942,25 @@ std::string GetWorkUnit(StratumClient& client)
mining_notify.push_back(Pair("method", "mining.notify"));
mining_notify.push_back(Pair("params", params));
// RandomX: the miner cannot derive the per-height RandomX key on its own (it depends on a block
// hash deep in the chain), so hand it the key bytes + height explicitly. Sent as its own
// mining.set_randomx_key message so the equihash-format mining.notify above stays byte-compatible
// with legacy miners; a RandomX miner reads this before hashing.
std::string randomx_key_msg;
if (StratumIsRandomX()) {
const std::string rxKey = GetRandomXKey(current_work.nHeight);
UniValue set_rxkey(UniValue::VOBJ);
set_rxkey.push_back(Pair("id", client.m_nextid++));
set_rxkey.push_back(Pair("method", "mining.set_randomx_key"));
UniValue rxparams(UniValue::VARR);
rxparams.push_back(HexStr(rxKey.begin(), rxKey.end())); // RandomX key bytes (hex)
rxparams.push_back(current_work.nHeight); // block height (sanity/logging)
set_rxkey.push_back(Pair("params", rxparams));
randomx_key_msg = set_rxkey.write() + "\n";
}
return GetExtraNonceRequest(client, job_id)
+ randomx_key_msg
+ set_target.write() + "\n"
+ mining_notify.write() + "\n";
}
@@ -942,8 +968,15 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
const std::vector<unsigned char>& extranonce1, const std::vector<unsigned char>& extranonce2,
boost::optional<uint32_t> nVersion, uint32_t nTime, const std::vector<unsigned char>& sol)
{
// Submit path handles BOTH proof-of-works, branched on StratumIsRandomX():
// * RandomX (DragonX): `sol` is the 32-byte RandomX hash and IS nSolution verbatim; validated
// via CheckRandomXSolution(&blkhdr, height). The target check (GetHash() < target) and the
// nNonce = extranonce1||extranonce2 assembly are identical to the equihash path.
// * Equihash (legacy): `sol` is the 1347-byte solution; the 3-byte zcash prefix is stripped
// and CheckEquihashSolution() validates it.
//
// called from stratum_mining_submit and uses following data, came from client:
// ["WORKER_NAME", "JOB_ID", "TIME", "NONCE_2", "EQUIHASH_SOLUTION"]
// ["WORKER_NAME", "JOB_ID", "TIME", "NONCE_2", "SOLUTION"]
// all other params we have saved in other places
if (extranonce1.size() + extranonce2.size() != 32) {
@@ -952,9 +985,9 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
throw JSONRPCError(RPC_INVALID_PARAMETER, msg);
}
// TODO: change hardcoded constants on actual determine of solution size, depends on equihash algo type: 200.9, etc.
if (sol.size() != 1347) {
std::string msg = strprintf("%s: solution is wrong length (received %d bytes; expected %d bytes", __func__, extranonce2.size(), 1347);
const size_t expected_sol_size = StratumIsRandomX() ? RX_STRATUM_SOLUTION_SIZE : 1347;
if (sol.size() != expected_sol_size) {
std::string msg = strprintf("%s: solution is wrong length (received %d bytes; expected %d bytes)", __func__, sol.size(), (int)expected_sol_size);
LogPrint("stratum", "%s\n", msg);
throw JSONRPCError(RPC_INVALID_PARAMETER, msg);
}
@@ -986,24 +1019,50 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
std::vector<unsigned char> nonce(extranonce1);
nonce.insert(nonce.end(), extranonce2.begin(), extranonce2.end());
blkhdr.nSolution = std::vector<unsigned char>(sol.begin() + 3, sol.end());
// RandomX: nSolution IS the 32-byte RandomX hash (verbatim). Equihash: strip the 3-byte
// zcash solution-size prefix.
blkhdr.nSolution = StratumIsRandomX() ? sol
: std::vector<unsigned char>(sol.begin() + 3, sol.end());
blkhdr.hashFinalSaplingRoot = current_work.GetBlock().hashFinalSaplingRoot;
blkhdr.hashMerkleRoot = current_work.GetBlock().hashMerkleRoot;
// Recompute from the coinbase CustomizeWork() just derived for THIS client. Reading the
// shared template's root would be wrong now that the template is left pristine, and was
// wrong before too -- it carried whichever client happened to request work first.
{
CBlock tmp(current_work.GetBlock());
tmp.vtx[0] = cb;
blkhdr.hashMerkleRoot = tmp.BuildMerkleTree();
}
blkhdr.nNonce = (uint256) nonce;
// example how to display constructed block
// if (instance_of_cstratumparams.fstdErrDebugOutput) {
// CBlockIndex index {blkhdr};
// index.SetHeight(current_work.nHeight);
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " blkhdr.hashPrevBlock = " << blkhdr.hashPrevBlock.GetHex() << std::endl;
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " blkhdr = " << blockToJSON(blkhdr, &index).write() << std::endl;
// }
// Cheap SHA256d filter first. This test used to sit *below* the RandomX verify, so 32
// arbitrary bytes from any peer bought a full ~65ms randomx_calculate_hash before anything
// rejected them -- on the shared HTTP/RPC libevent thread, and holding the global
// cs_randomx_validator that block validation also takes. GetHash() is SerializeHash over the
// header including nSolution, so passing this costs real grinding. Semantics are unchanged:
// an empty local_diff still parses to zero and still rejects, exactly as before.
if (!instance_of_cstratumparams.fAllowLowDiffShares &&
UintToArith256(blkhdr.GetHash()) > arith_uint256(current_work.local_diff)) {
CBlockIndex diff_index;
diff_index.nBits = UintToArith256(blkhdr.GetHash()).GetCompact();
const double share_diff = GetDifficulty(&diff_index);
diff_index.nBits = arith_uint256(current_work.local_diff).GetCompact();
const double target_diff = GetDifficulty(&diff_index);
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Low diff share (diff %g, local %g)", share_diff, target_diff));
}
// block is constructed, now it's time to VerifyEH
if (instance_of_cstratumparams.fCheckEquihashSolution && !CheckEquihashSolution(&blkhdr, Params()))
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid equihash solution");
if (StratumIsRandomX()) {
// Verify the submitted 32-byte solution really is the RandomX hash of this header
// (nSolution == randomx_hash(GetRandomXInput(blkhdr), GetRandomXKey(height))). This is the
// consensus authority for the solution; without it a miner could submit a low-GetHash()
// block with a bogus nSolution. Rejects fake shares before we count/relay them.
if (!CheckRandomXSolution(&blkhdr, current_work.nHeight))
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid RandomX solution");
} else if (instance_of_cstratumparams.fCheckEquihashSolution && !CheckEquihashSolution(&blkhdr, Params())) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid equihash solution");
}
arith_uint256 bnTarget; bool fNegative, fOverflow;
bnTarget.SetCompact(blkhdr.nBits, &fNegative, &fOverflow);
@@ -1018,7 +1077,6 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
uint8_t pubkey33[33]; int32_t height = current_work.nHeight;
res = CheckProofOfWork(blkhdr, pubkey33, height, Params().GetConsensus());
}
// if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << "res[1] = " << res << std::endl;
uint256 hash = blkhdr.GetHash();
@@ -1050,10 +1108,8 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
std::chrono::duration<double, std::milli> elapsed;
uint64_t shares_accepted_since_last;
// TODO: we need to check hash > local port diff, and if it's true -> throw an exception -> diff too low (!)
if (!instance_of_cstratumparams.fAllowLowDiffShares)
if (UintToArith256(blkhdr.GetHash()) > arith_uint256(current_work.local_diff))
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Low diff share (diff %g, local %g)", hush_real_diff, hush_local_diff));
// (the low-diff share check moved above the RandomX verify -- see SubmitBlock's cheap
// SHA256d filter -- so that attacker-controlled bytes cannot buy a RandomX hash)
if (finish > start)
{
@@ -1061,27 +1117,8 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
shares_accepted_since_last = counter_TotalShares - counter_prev;
start = finish;
counter_prev = counter_TotalShares;
// std::cerr << strprintf("%f ms - %" PRIu64 "", elapsed.count(), shares_accepted_since_last) << std::endl;
}
bool fDisplayDiffHUSH = true; // otherwise it will display ccminer diff
std::cerr << DateTimeStrPrecise() <<
strprintf("%saccepted: %" PRIu64 "/%" PRIu64 "%s ", ColorTypeNames[cl_WHT], counter_TotalBlocks, counter_TotalShares, ColorTypeNames[cl_N] );
if (fDisplayDiffHUSH) {
/* hushd diff display */
std::cerr << strprintf("%slocal %g%s ", "\x1B[90m", hush_local_diff, ColorTypeNames[cl_N]) <<
strprintf("%s(diff %g, target %g) %s ", ColorTypeNames[cl_WHT], hush_real_diff, hush_target_diff, ColorTypeNames[cl_N]);
} else { /* ccminer diff display */
std::cerr << strprintf("%slocal %.3f%s ", "\x1B[90m", ccminer_local_diff, ColorTypeNames[cl_N]) <<
strprintf("%s(diff %.3f, target %.3f) %s", ColorTypeNames[cl_WHT], ccminer_real_diff, ccminer_target_diff, ColorTypeNames[cl_N]); // ccminer diff
}
std::cerr << "" <<
strprintf("%f ms ", elapsed.count()) << // 1 share took elapsed ms
strprintf("%s%s%s ", ColorTypeNames[cl_LGR], (res ? "yay!!!": "yes!"), ColorTypeNames[cl_N]) <<
std::endl;
// (diff %g, target %g), %
if (res) {
@@ -1097,22 +1134,14 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
block.nVersion = version;
// block.hashMerkleRoot = BlockMerkleRoot(block);
block.hashMerkleRoot = block.BuildMerkleTree();
//if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << "hashMerkleRoot = " << block.hashMerkleRoot.GetHex() << std::endl;
block.nTime = nTime;
// block.nNonce = nNonce;
// nNonce <<= 32; nNonce >>= 16; // clear the top and bottom 16 bits (for local use as thread flags and counters)
block.nNonce = (uint256) nonce;
block.nSolution = std::vector<unsigned char>(sol.begin() + 3, sol.end());
// example how to pre-check the equihash solution
// if(instance_of_cstratumparams.fstdErrDebugOutput) {
// CBlockIndex index {blkhdr};
// index.SetHeight(-1);
// std::cerr << "block = " << blockToJSON(block, &index, true).write(1) << std::endl;
// std::cerr << "CheckEquihashSolution = " << CheckEquihashSolution(&block, Params()) << std::endl;
// }
block.nSolution = StratumIsRandomX() ? sol
: std::vector<unsigned char>(sol.begin() + 3, sol.end());
// std::shared_ptr<const CBlock> pblock = std::make_shared<const CBlock>(block);
// res = ProcessNewBlock(Params(), pblock, true, NULL);
@@ -1120,8 +1149,6 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork
CValidationState state;
res = ProcessNewBlock(0,0,state, NULL, &block, true /* forceProcessing */ , NULL);
//if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << "res[2] = " << res << std::endl;
// we haven't PreciousBlock, so we can't prioritize the block this way for now
/*
if (res) {
@@ -1207,14 +1234,6 @@ UniValue stratum_mining_subscribe(StratumClient& client, const UniValue& params)
* sExtraNonce1 for a given client based on m_secret.
*/
// if (instance_of_cstratumparams.fstdErrDebugOutput && vExtraNonce1.size() > 3) {
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " " << strprintf("client.m_supports_extranonce = %d, [%d, %d, %d, %d], %s", client.m_supports_extranonce, vExtraNonce1[0], vExtraNonce1[1], vExtraNonce1[2], vExtraNonce1[3], sExtraNonce1) << std::endl;
// // recalc from client.m_secret example
// uint256 sha256;
// CSHA256().Write(client.m_secret.begin(), 32).Finalize(sha256.begin());
// std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " " << HexStr(std::vector<unsigned char>(sha256.begin(), sha256.begin() + 4)) << std::endl;
// }
ret.push_back(NullUniValue);
ret.push_back(sExtraNonce1);
@@ -1261,7 +1280,7 @@ UniValue stratum_mining_authorize(StratumClient& client, const UniValue& params)
// This means a miner can run a private pool without TLS and not
// worry about MITM attacks that change addresses, and leaks less metadata.
// It also means many miners can be used and updating their mining address does not
// require any changes on each miner, just restart hushd with a new -stratumaddress
// require any changes on each miner, just restart dragonxd with a new -stratumaddress
if(addr.ToString() == "x") {
addr = CBitcoinAddress(GetArg("-stratumaddress", ""));
const std::string msg = strprintf("%s: Authorized client with default stratum address=%s", __func__, addr.ToString());
@@ -1269,9 +1288,9 @@ UniValue stratum_mining_authorize(StratumClient& client, const UniValue& params)
}
if (!addr.IsValid()) {
const std::string msg = strprintf("%s: Invalid Hush address=%s", __func__, addr.ToString());
const std::string msg = strprintf("%s: Invalid DragonX address=%s", __func__, addr.ToString());
LogPrint("stratum", "%s\n", msg);
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid Hush address: %s", username));
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid DragonX address: %s", username));
}
client.m_addr = addr;
@@ -1315,7 +1334,11 @@ UniValue stratum_mining_configure(StratumClient& client, const UniValue& params)
UniValue stratum_mining_submit(StratumClient& client, const UniValue& params)
{
// {"id": 4, "method": "mining.submit", "params": ["WORKER_NAME", "JOB_ID", "TIME", "NONCE_2", "EQUIHASH_SOLUTION"]}\n
// Share submission. On RandomX (DragonX) the SOLUTION param is the 32-byte RandomX hash; on
// Equihash (legacy) it is the 1347-byte solution. The size is validated below and the branch is
// handled in SubmitBlock(). NONCE_2 is the miner-chosen tail of the 32-byte block nNonce.
//
// {"id": 4, "method": "mining.submit", "params": ["WORKER_NAME", "JOB_ID", "TIME", "NONCE_2", "SOLUTION"]}\n
// NONCE_1 is first part of the block header nonce (in hex).
// By protocol, Zcash's nonce is 32 bytes long. The miner will pick NONCE_2 such that len(NONCE_2) = 32 - len(NONCE_1). Please note that Stratum use hex encoding, so you have to convert NONCE_1 from hex to binary before.
@@ -1334,20 +1357,39 @@ UniValue stratum_mining_submit(StratumClient& client, const UniValue& params)
const std::string method("mining.submit");
BoundParams(method, params, 5,5);
// Parity with every other handler (GetWorkUnit, mining.aux.*, mining.extranonce.*), which all
// refuse an unauthorized client. NOTE this is not authentication: mining.authorize validates no
// credential, so it only costs an attacker one extra line. It is here so the submit path cannot
// be reached without at least completing the handshake; the cheap-target check below is what
// actually bounds the work an unknown peer can force.
if (!client.m_authorized && client.m_aux_addr.empty()) {
const std::string msg = strprintf("%s: share submitted by an unauthorized client", __func__);
LogPrint("stratum", "%s\n", msg);
throw JSONRPCError(RPC_INVALID_REQUEST, "Stratum client not authorized. Use mining.authorize first, with a DragonX R.. address as the username or 'x' to mine to the default address.");
}
// First parameter is the client username, which is ignored.
/* EWBF 31 bytes job_id fix */
bool fEWBFJobIDFixNeeded = false;
uint256 ret;
if (params[1].isStr()) {
//std::cerr << "\"" << params[1].get_str() << "\"" << std::endl;
const std::string job_id_str = params[1].get_str();
const std::string hexDigits = "0123456789abcdef";
// std::cerr << strprintf("\"%s\" (%d)", job_id_str, job_id_str.length()) << std::endl;
if (job_id_str.length() == 63) {
fEWBFJobIDFixNeeded = true;
for(const auto& hexDigit : hexDigits) {
ret = uint256(ParseHex(job_id_str + hexDigit));
// ParseHex() stops at the first non-hex character and returns a SHORT vector
// without signalling an error, and base_blob(const std::vector<unsigned char>&)
// asserts vch.size() == 32. Constructing without checking therefore lets any
// 63-character job_id containing a non-hex byte abort the daemon -- from an
// unauthenticated client, before any other validation. Skip bad candidates
// instead; if none of the 16 completions parse, ret stays null, misses
// work_templates below, and the handler returns false cleanly.
std::vector<unsigned char> vch = ParseHex(job_id_str + hexDigit);
if (vch.size() != 32) continue;
ret = uint256(vch);
if (work_templates.count(ret)) break;
}
}
@@ -1370,8 +1412,9 @@ UniValue stratum_mining_submit(StratumClient& client, const UniValue& params)
uint32_t nTime = bswap_32(ParseHexInt4(params[2], "nTime"));
std::vector<unsigned char> sol = ParseHexV(params[4], "solution");
if (sol.size() != 1347) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("solution is wrong length (received %d bytes; expected %d bytes", sol.size(), 1347));
const size_t expected_sol_size = StratumIsRandomX() ? RX_STRATUM_SOLUTION_SIZE : 1347;
if (sol.size() != expected_sol_size) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("solution is wrong length (received %d bytes; expected %d bytes)", sol.size(), (int)expected_sol_size));
}
std::vector<unsigned char> extranonce1 = client.ExtraNonce1(job_id);
@@ -1816,7 +1859,7 @@ void SendKeepAlivePackets()
if ( (client.m_last_tip && client.m_last_tip->GetHeight() == chainActive.Tip()->GetHeight()) || (!client.m_last_tip) )
{
LOCK(cs_stratum);
std::cerr << DateTimeStrPrecise() << "\033[31m" << client.m_from.ToString() << "\033[0m seems stucked (ccminer issue), need to emulate new block incoming to unstuck!" << std::endl;
LogPrint("stratum", "%s seems stucked (ccminer issue), need to emulate new block incoming to unstuck!\n", client.m_from.ToString());
mempool.AddTransactionsUpdated(1);
client.m_last_tip = (client.m_last_tip ? nullptr : chainActive.Tip());
client.m_nextid++;
@@ -1829,15 +1872,25 @@ void SendKeepAlivePackets()
}
/** Configure the Hush stratum server */
/** Configure the DragonX stratum server */
bool InitStratumServer()
{
LOCK(cs_stratum);
int stratumPort = BaseParams().StratumPort();
int defaultPort = GetArg("-stratumport", stratumPort);
fprintf(stderr,"%s: Starting built-in stratum server on port %d\n",__func__, defaultPort );
LogPrintf("%s: Starting built-in stratum server on port %d\n",__func__, defaultPort );
// Optional pool share-target override (64-hex, big-endian like getblocktemplate's "target").
// Loosens/tightens the accepted share difficulty; also lets a solo/test miner accept easy shares
// on a low-difficulty chain (default is the diff-1 target 00ffff00..). Larger value = easier.
if (mapArgs.count("-stratumtarget")) {
const std::string t = GetArg("-stratumtarget", "");
if (!t.empty()) {
instance_of_cstratumparams.setTarget(arith_uint256(t));
LogPrintf("%s: stratum pool share target overridden to %s\n", __func__, t);
}
}
if (!InitStratumAllowList(stratum_allow_subnets)) {
LogPrint("stratum", "Unable to bind stratum server to an endpoint.\n");
@@ -1959,7 +2012,7 @@ UniValue rpc_stratum_updatework(const UniValue& params, bool fHelp, const CPubKe
// Ignore clients that aren't authorized yet.
if (!client.m_authorized && client.m_aux_addr.empty()) {
fprintf(stderr,"%s: Ignoring unauthorized client\n", __func__);
LogPrint("stratum", "%s: Ignoring unauthorized client\n", __func__);
continue;
}

View File

@@ -285,8 +285,9 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockF
std::pair<char, uint256> key = make_pair(DB_BLOCK_INDEX, it->GetBlockHash());
try {
CDiskBlockIndex dbindex {it, [this, &key]() {
// It can happen that the index entry is written, then the Equihash solution is cleared from memory,
// It can happen that the index entry is written, then the solution is cleared from memory,
// then the index entry is rewritten. In that case we must read the solution from the old entry.
// (GetSolution() returns DragonX's RandomX solution.)
CDiskBlockIndex dbindex_old;
if (!Read(key, dbindex_old)) {
LogPrintf("%s: Failed to read index entry", __func__);
@@ -472,7 +473,6 @@ bool CBlockTreeDB::Snapshot2(std::map <std::string, CAmount> &addressAmounts, Un
iter->GetKey(keyObj);
char chType = keyObj.first;
CAddressIndexIteratorKey indexKey = keyObj.second;
//fprintf(stderr, "chType=%d\n", chType);
if (chType == DB_ADDRESSUNSPENTINDEX)
{
try {
@@ -485,7 +485,7 @@ bool CBlockTreeDB::Snapshot2(std::map <std::string, CAmount> &addressAmounts, Un
std::map <std::string, int>::iterator ignored = ignoredMap.find(address);
if (ignored != ignoredMap.end())
{
fprintf(stderr,"ignoring %s\n", address.c_str());
LogPrint("coindb", "ignoring %s\n", address.c_str());
ignoredAddresses++;
continue;
}
@@ -493,17 +493,14 @@ bool CBlockTreeDB::Snapshot2(std::map <std::string, CAmount> &addressAmounts, Un
if ( pos == addressAmounts.end() )
{
// insert new address + utxo amount
//fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue);
addressAmounts[address] = nValue;
totalAddresses++;
}
else
{
// update unspent tally for this address
//fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue);
addressAmounts[address] += nValue;
}
//fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN);
// total += nValue;
utxos++;
total += nValue;
@@ -517,12 +514,17 @@ bool CBlockTreeDB::Snapshot2(std::map <std::string, CAmount> &addressAmounts, Un
}
catch (const std::exception& e)
{
fprintf(stderr, "DONE reading index entries\n");
break;
// A genuine deserialization/LevelDB error here is NOT normal completion:
// the for-loop's iter->Valid() already handles end-of-iteration, and
// non-address key types are skipped by the chType check above. Swallowing
// the exception and building a snapshot from partial data is wrong. Fail
// like the inner catch, which the author marked consensus-relevant
// ("we need to exit here if so for consensus code!").
fprintf(stderr, "%s: LevelDB index iteration exception! - %s\n", __func__, e.what());
return false;
}
}
//fprintf(stderr, "total=%f, totalAddresses=%li, utxos=%li, ignored=%li\n", (double) total / COIN, totalAddresses, utxos, ignoredAddresses);
// this is for the snapshot RPC, you can skip this by passing a 0 as the last argument.
if (ret)
{
@@ -675,23 +677,18 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
pcursor->Seek(make_pair(DB_BLOCK_INDEX, uint256()));
//fprintf(stderr,"%s: Seeked cursor to block index\n",__FUNCTION__);
// Load mapBlockIndex
while (pcursor->Valid()) {
//fprintf(stderr,"%s: Valid cursor\n",__FUNCTION__);
boost::this_thread::interruption_point();
std::pair<char, uint256> key;
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
//fprintf(stderr,"%s: Found DB_BLOCK_INDEX\n",__FUNCTION__);
CDiskBlockIndex diskindex;
if (pcursor->GetValue(diskindex)) {
// Construct block index object
//fprintf(stderr,"%s: Creating CBlockIndex...\n",__FUNCTION__);
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
pindexNew->SetHeight(diskindex.GetHeight());
//fprintf(stderr,"%s: Setting CBlockIndex height...\n",__FUNCTION__);
pindexNew->nFile = diskindex.nFile;
pindexNew->nDataPos = diskindex.nDataPos;
pindexNew->nUndoPos = diskindex.nUndoPos;
@@ -702,14 +699,13 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pindexNew->nTime = diskindex.nTime;
pindexNew->nBits = diskindex.nBits;
pindexNew->nNonce = diskindex.nNonce;
// the Equihash solution will be loaded lazily from the dbindex entry
// the solution (DragonX RandomX solution) will be loaded lazily from the dbindex entry
// pindexNew->nSolution = diskindex.nSolution;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nCachedBranchId = diskindex.nCachedBranchId;
pindexNew->nTx = diskindex.nTx;
pindexNew->nSproutValue = diskindex.nSproutValue;
pindexNew->nSaplingValue = diskindex.nSaplingValue;
//fprintf(stderr,"%s: Setting CBlockIndex details...\n",__FUNCTION__);
pindexNew->segid = diskindex.segid;
pindexNew->nNotaryPay = diskindex.nNotaryPay;
pindexNew->nPayments = diskindex.nPayments;
@@ -725,7 +721,6 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pindexNew->nFullyShieldedPayments = diskindex.nFullyShieldedPayments;
pindexNew->nNotarizations = diskindex.nNotarizations;
//fprintf(stderr,"loadguts ht.%d\n",pindexNew->GetHeight());
// Consistency checks
/*
CBlockHeader header;

View File

@@ -499,27 +499,35 @@ boost::filesystem::path GetDefaultDataDir()
if ( SMART_CHAIN_SYMBOL[0] != 0 )
strcpy(symbol,SMART_CHAIN_SYMBOL);
else symbol[0] = 0;
// OLD NAMES:
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Komodo
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Komodo
// Mac: ~/Library/Application Support/Komodo
// Unix: ~/.komodo
// DragonX stores its data under a per-chain subdirectory named after
// SMART_CHAIN_SYMBOL (which is "DRAGONX"), so the default datadir resolves
// to (Unix) ~/.hush/DRAGONX, (Mac) ~/Library/Application Support/Hush/DRAGONX,
// or (Windows) %APPDATA%\Hush\DRAGONX.
//
// The "Hush" / "Komodo" parent-directory names below are retained from the
// Hush/Komodo lineage: the ".hush"/"Hush" path is the current location, and
// the ".komodo"/"Komodo" path is only probed as a backward-compatible
// fallback for pre-existing legacy data directories. Do not change these
// string literals -- they determine where node data is read from and written.
// NEW NAMES:
// Current (per-symbol subdirectory lives under these parents):
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Hush
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Hush
// Mac: ~/Library/Application Support/Hush
// Unix: ~/.hush
// ~/.hush was actually used by the original 1.x version of Hush, but we will
// only make subdirectories inside of it, so we won't be able to overwrite
// an old wallet.dat from the Ice Ages :)
// Legacy fallback (only used if such a directory already exists):
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Komodo
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Komodo
// Mac: ~/Library/Application Support/Komodo
// Unix: ~/.komodo
fs::path pathRet;
#ifdef _WIN32
// Windows
pathRet = GetSpecialFolderPath(CSIDL_APPDATA) / "Hush" / symbol;
// Always use .hush/HUSH3, if it exists (even if .komodo/HUSH3 exists)
// Always use Hush\<symbol> (Hush\DRAGONX) if it exists, even if the legacy
// Komodo\<symbol> directory also exists.
if(fs::is_directory(pathRet)) {
return pathRet;
} else {
@@ -528,7 +536,7 @@ boost::filesystem::path GetDefaultDataDir()
// existing legacy directory, use that for backward compat
return pathRet;
} else {
// For new clones, use Hush/ACNAME
// For new nodes, use Hush\<symbol>
pathRet = GetSpecialFolderPath(CSIDL_APPDATA) / "Hush" / symbol;
return pathRet;
}
@@ -551,7 +559,7 @@ boost::filesystem::path GetDefaultDataDir()
// create Library/Application Support/Hush if it doesn't exist
TryCreateDirectory(tmppath);
// Always use Hush/HUSH3 if it exists
// Always use Hush/<symbol> (Hush/DRAGONX) if it exists
if(fs::is_directory(tmppath / symbol)) {
return tmppath / symbol;
} else {
@@ -563,16 +571,16 @@ boost::filesystem::path GetDefaultDataDir()
// Found legacy dir, use that
return tmppath / symbol;
} else {
// For new clones, use Hush/ACNAME
// For new nodes, use Hush/<symbol>
tmppath = pathRet / "Hush" / symbol;
}
return tmppath;
}
#else
// Unix
// New directory :)
// Unix: current default datadir is ~/.hush/<symbol> (i.e. ~/.hush/DRAGONX)
fs::path tmppath = pathRet / ".hush" / symbol;
// Always use .hush/HUSH3, if it exists (even if .komodo/HUSH3 exists)
// Always use ~/.hush/<symbol> (~/.hush/DRAGONX) if it exists, even if the
// legacy ~/.komodo/<symbol> directory also exists.
if(fs::is_directory(tmppath)) {
return tmppath;
} else {
@@ -582,7 +590,7 @@ boost::filesystem::path GetDefaultDataDir()
// existing legacy directory, use that for backward compat
return tmppath;
} else {
// For new clones, use .hush/ACNAME
// For new nodes, use ~/.hush/<symbol>
tmppath = pathRet / ".hush" / symbol;
}
return tmppath;
@@ -598,13 +606,17 @@ static CCriticalSection csPathCached;
static boost::filesystem::path ZC_GetBaseParamsDir()
{
// Copied from GetDefaultDataDir and adapted for zcash params.
// Copied from GetDefaultDataDir and adapted for the zk-SNARK parameter files.
// DragonX reuses the upstream Sapling parameter directory layout, so these
// locations retain the historical "ZcashParams" / ".zcash-params" names. Do
// not change these string literals -- they determine where the proving and
// verifying keys are loaded from.
namespace fs = boost::filesystem;
// Windows < Vista: C:\Documents and Settings\Username\Application Data\ZcashParams
// Windows >= Vista: C:\Users\Username\AppData\Roaming\ZcashParams
// Mac: ~/Library/Application Support/ZcashParams
// Unix: ~/.zcash-params
// Debian packages: /usr/share/hush
// System-wide install (Debian packages): /usr/share/hush
fs::path pathRet;
#ifdef _WIN32
return GetSpecialFolderPath(CSIDL_APPDATA) / "ZcashParams";

View File

@@ -34,10 +34,9 @@ static const size_t AUTOSHIELD_TX_OVERHEAD = (3 * AUTOSHIELD_SAPLING_OUTPUT_SIZE
// conservative, so an under-estimate translates directly into an oversize tx.
// The remainder is simply shielded on the next round.
static const size_t AUTOSHIELD_MAX_INPUTS = 400;
// Unrelated to the cap above despite sharing the value: this is a SIZE IN BYTES for
// one spent P2SH input, mirroring CTXIN_SPEND_P2SH_SIZE in rpcwallet.cpp.
static const size_t AUTOSHIELD_CTXIN_P2SH_SIZE = 400;
// Expire unmined autoshield txs after this many blocks, so a tx cannot straddle
// a network-upgrade activation.
static const int AUTOSHIELD_EXPIRY_DELTA = 15;
AsyncRPCOperation_autoshieldcoinbase::AsyncRPCOperation_autoshieldcoinbase(int targetHeight)
: targetHeight_(targetHeight) {}
@@ -64,23 +63,8 @@ void AsyncRPCOperation_autoshieldcoinbase::main() {
try {
success = main_impl();
} catch (const UniValue& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
set_error_from_current_exception();
}
stop_execution_clock();
@@ -105,50 +89,34 @@ void AsyncRPCOperation_autoshieldcoinbase::main() {
getId(), getStateAsString(), numTxCreated_, FormatMoney(amountShielded_));
}
// Resolve the Sapling destination for auto-shielded coinbase.
//
// Recoverability is the hard requirement: coinbase we shield must land in an
// address that a bare -mnemonic/-hdseed restore of THIS wallet's seed re-derives
// on its own. A restore pre-derives exactly -mnemonicsaplinggap sapling accounts
// starting at index 0, with saplingAccountCounter reset to 0 (init.cpp:2349-2355),
// so the only self-recoverable destinations are the default addresses of
// m/32'/<coin>'/i' for i < gap.
//
// We therefore DERIVE those accounts from the seed and pick the lowest index the
// wallet already holds. Deriving is the only authoritative test. In particular
// CKeyMetadata is NOT evidence of provenance: z_importkey / z_importwallet copy
// both hdKeypath and seedFp verbatim out of the import source
// (wallet.cpp:5522-5529 <- rpcdump.cpp:511-516), so a foreign key can claim any
// keypath and any seed fingerprint. Filtering on metadata would let an imported
// key win as "account 0" and silently receive every shielded reward.
//
// Caller must hold cs_wallet and must already have checked the wallet is unlocked.
bool AsyncRPCOperation_autoshieldcoinbase::resolveDestination(
libzcash::SaplingPaymentAddress& destOut, std::string& destStrOut) {
// Read-only half of destination resolution, shared with init.cpp so the answer to
// "where will auto-shielding send?" is available before the first round runs rather
// than only after one has fired. Mutates nothing: no key generation, no caching.
AutoShieldDestStatus ResolveAutoShieldDestinationReadOnly(
libzcash::SaplingPaymentAddress& destOut, std::string& destStrOut, uint32_t& accountOut) {
accountOut = AUTOSHIELD_ACCOUNT_NONE;
// 1. Explicit -autoshieldaddress override (validated as a spendable Sapling
// zaddr at init.cpp:2494-2506). This also serves as the per-process cache
// for whatever step 2/3 resolved.
// zaddr in init.cpp). This doubles as the per-process cache for whatever
// the derivation below resolved on an earlier round.
if (!pwalletMain->autoShieldAddress.empty()) {
auto decoded = DecodePaymentAddress(pwalletMain->autoShieldAddress);
if (boost::get<libzcash::SaplingPaymentAddress>(&decoded) != nullptr) {
destOut = boost::get<libzcash::SaplingPaymentAddress>(decoded);
destStrOut = pwalletMain->autoShieldAddress;
return true;
return AutoShieldDestStatus::Resolved;
}
LogPrintf("%s: configured -autoshieldaddress is not a valid Sapling address\n", getId());
return false;
return AutoShieldDestStatus::InvalidOverride;
}
// 2. Walk the restore window m/32'/coin'/[0, gap)' derived from the seed.
HDSeed seed;
if (!pwalletMain->GetHDSeedForDerivation(seed)) {
LogPrintf("%s: no HD seed available; refusing to pick an autoshield destination\n", getId());
return false;
return AutoShieldDestStatus::NoSeed;
}
// Mirror init.cpp:2349-2350's own clamp, and cap into the hardened index
// space so (i | ZIP32_HARDENED_KEY_LIMIT) below stays well formed.
// Mirror init.cpp's own clamp, and cap into the hardened index space so
// (i | ZIP32_HARDENED_KEY_LIMIT) below stays well formed.
int64_t gapArg = GetArg("-mnemonicsaplinggap", 100);
if (gapArg < 0) {
gapArg = 0;
@@ -179,15 +147,77 @@ bool AsyncRPCOperation_autoshieldcoinbase::resolveDestination(
if (pwalletMain->GetSaplingExtendedSpendingKey(addr, held)) {
destOut = addr;
destStrOut = EncodePaymentAddress(addr);
// Cache for the life of the process; step 1 short-circuits later
// rounds. Safe: we only cache post-validation.
pwalletMain->autoShieldAddress = destStrOut;
LogPrintf("%s: autoshield destination %s (seed-derived sapling account %u, gap %u)\n",
getId(), destStrOut, (unsigned)i, (unsigned)saplingGap);
return true;
accountOut = i;
return AutoShieldDestStatus::Resolved;
}
}
return AutoShieldDestStatus::NotFound;
}
// Resolve the Sapling destination for auto-shielded coinbase.
//
// Recoverability is the hard requirement: coinbase we shield must land in an
// address that a bare -mnemonic/-hdseed restore of THIS wallet's seed re-derives
// on its own. A restore pre-derives exactly -mnemonicsaplinggap sapling accounts
// starting at index 0, with saplingAccountCounter reset to 0 (init.cpp:2349-2355),
// so the only self-recoverable destinations are the default addresses of
// m/32'/<coin>'/i' for i < gap.
//
// We therefore DERIVE those accounts from the seed and pick the lowest index the
// wallet already holds. Deriving is the only authoritative test. In particular
// CKeyMetadata is NOT evidence of provenance: z_importkey / z_importwallet copy
// both hdKeypath and seedFp verbatim out of the import source
// (wallet.cpp:5522-5529 <- rpcdump.cpp:511-516), so a foreign key can claim any
// keypath and any seed fingerprint. Filtering on metadata would let an imported
// key win as "account 0" and silently receive every shielded reward.
//
// Caller must hold cs_wallet and must already have checked the wallet is unlocked.
bool AsyncRPCOperation_autoshieldcoinbase::resolveDestination(
libzcash::SaplingPaymentAddress& destOut, std::string& destStrOut) {
uint32_t account = AUTOSHIELD_ACCOUNT_NONE;
switch (ResolveAutoShieldDestinationReadOnly(destOut, destStrOut, account)) {
case AutoShieldDestStatus::Resolved:
// Cache for the life of the process; the override branch of the resolver
// short-circuits later rounds. Safe: we only cache post-validation.
pwalletMain->autoShieldAddress = destStrOut;
if (account == AUTOSHIELD_ACCOUNT_NONE) {
LogPrintf("%s: autoshield destination %s (configured)\n", getId(), destStrOut);
} else {
LogPrintf("%s: autoshield destination %s (seed-derived sapling account %u)\n",
getId(), destStrOut, (unsigned)account);
}
return true;
case AutoShieldDestStatus::InvalidOverride:
LogPrintf("%s: configured -autoshieldaddress is not a valid Sapling address\n", getId());
return false;
case AutoShieldDestStatus::NoSeed:
LogPrintf("%s: no HD seed available; refusing to pick an autoshield destination\n", getId());
return false;
case AutoShieldDestStatus::NotFound:
break; // nothing in the window yet: fall through and derive one
}
// Re-establish the derivation context the resolver used, for step 3 below.
HDSeed seed;
if (!pwalletMain->GetHDSeedForDerivation(seed)) {
LogPrintf("%s: no HD seed available; refusing to pick an autoshield destination\n", getId());
return false;
}
int64_t gapArg = GetArg("-mnemonicsaplinggap", 100);
if (gapArg < 0) {
gapArg = 0;
}
if (gapArg > (int64_t)ZIP32_HARDENED_KEY_LIMIT) {
gapArg = (int64_t)ZIP32_HARDENED_KEY_LIMIT;
}
const uint32_t saplingGap = (uint32_t)gapArg;
const uint32_t bip44CoinType = Params().BIP44CoinType();
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT);
auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT);
// 3. Nothing usable in the window yet: derive the next account, but only if
// GenerateNewSaplingZKey will land INSIDE the window. It does NOT derive
// at saplingAccountCounter: its do/while skips every index whose spending
@@ -262,7 +292,7 @@ bool AsyncRPCOperation_autoshieldcoinbase::main_impl() {
// from below), not the stale enqueue-time targetHeight_, so a queue delay
// cannot slip a straddling expiry past this guard.
auto nextActivationHeight = NextActivationHeight(tipHeight, consensusParams);
if (nextActivationHeight && tipHeight + AUTOSHIELD_EXPIRY_DELTA >= nextActivationHeight.get()) {
if (nextActivationHeight && tipHeight + AUTO_OP_EXPIRY_DELTA >= nextActivationHeight.get()) {
LogPrintf("%s: autoshield tx could expire across a NU activation. Skipping this round.\n", opid);
return true;
}
@@ -310,9 +340,10 @@ bool AsyncRPCOperation_autoshieldcoinbase::main_impl() {
}
// Gather matured, spendable coinbase UTXOs, byte-capped to a single tx.
// AvailableCoins with fOnlySpendable already excludes immature coinbase
// (< COINBASE_MATURITY) and outputs we don't own, so external
// -mineraddress / pool coinbase naturally yields zero inputs.
// AvailableCoins excludes immature coinbase unconditionally (wallet.cpp,
// `IsCoinBase() && GetBlocksToMaturity() > 0`) and only ever returns outputs
// we own, so external -mineraddress / pool coinbase yields zero inputs. The
// second argument here is fOnlyConfirmed, not fOnlySpendable.
size_t estimatedTxSize = AUTOSHIELD_TX_OVERHEAD;
std::vector<COutput> vecOutputs;
pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, true);
@@ -375,8 +406,13 @@ bool AsyncRPCOperation_autoshieldcoinbase::main_impl() {
// Build the t->z shield tx. Proof generation happens in Build() WITHOUT
// holding cs_wallet (mirrors the sweep op) so we don't stall wallet RPCs.
auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain);
builder.SetExpiryHeight(tipHeight + AUTOSHIELD_EXPIRY_DELTA);
// tipHeight, not targetHeight_: the builder's height selects the consensus
// branch id (transaction_builder.cpp CurrentEpochBranchId), and the NU-straddle
// guard above plus SetExpiryHeight below are both keyed off tipHeight. Using the
// stale enqueue-time height here meant the guard was checking a height the
// transaction was not actually signed against.
auto builder = TransactionBuilder(consensusParams, tipHeight, pwalletMain);
builder.SetExpiryHeight(tipHeight + AUTO_OP_EXPIRY_DELTA);
builder.SetFee(fee);
for (const auto& t : inputs) {
@@ -439,6 +475,13 @@ void AsyncRPCOperation_autoshieldcoinbase::setResult() {
}
void AsyncRPCOperation_autoshieldcoinbase::cancel() {
// Cancelling is how the scheduler stops an in-flight round, so unlike the base
// class this must be able to move an EXECUTING operation to CANCELLED. What it
// must not do is overwrite a state that is already terminal: the scheduler
// cancels the previous operation when it enqueues the next one, and that one may
// have already SUCCEEDED, whose result would otherwise be relabelled as cancelled.
if (isSuccess() || isFailed() || isCancelled())
return;
set_state(OperationStatus::CANCELLED);
}

View File

@@ -11,8 +11,25 @@
#include "zcash/Address.hpp"
#include "zcash/zip32.h"
// Default fee for automatic coinbase-shielding transactions
static const CAmount DEFAULT_AUTOSHIELD_FEE = 10000;
// Sentinel for "not a derived account" (i.e. the configured -autoshieldaddress).
static const uint32_t AUTOSHIELD_ACCOUNT_NONE = UINT32_MAX;
enum class AutoShieldDestStatus {
Resolved, // destOut/destStrOut are set
NotFound, // no in-gap account held yet; the operation will derive one
InvalidOverride, // -autoshieldaddress is set but is not a Sapling address
NoSeed, // no HD seed available (e.g. locked wallet)
};
// Resolve the auto-shield destination WITHOUT mutating the wallet: the configured
// -autoshieldaddress if set, else the lowest in-gap seed-derived account the wallet
// already holds. It deliberately does NOT generate a key, so init can call it purely
// to answer "where will this send?" -- deriving a fresh account as a side effect of
// populating a status field would be wrong. The operation's own resolveDestination
// falls through to generation when this returns NotFound.
// Caller must hold cs_wallet.
AutoShieldDestStatus ResolveAutoShieldDestinationReadOnly(
libzcash::SaplingPaymentAddress& destOut, std::string& destStrOut, uint32_t& accountOut);
// A periodic, wallet-local operation that drains matured *transparent* coinbase
// UTXOs into a wallet-owned Sapling z-address in size-bounded batches. It is the

View File

@@ -133,23 +133,8 @@ void AsyncRPCOperation_mergetoaddress::main()
try {
success = main_impl();
} catch (const UniValue& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
set_error_from_current_exception();
}
#ifdef ENABLE_MINING

View File

@@ -19,7 +19,12 @@
CAmount fConsolidationTxFee = DEFAULT_CONSOLIDATION_FEE;
bool fConsolidationMapUsed = false;
const int CONSOLIDATION_EXPIRY_DELTA = 15;
// Number of Sietch dummy ("zdust") shielded outputs added to every consolidation
// transaction to obscure the real output and keep the anonymity set large. This is
// a wallet privacy-tuning parameter (not a consensus rule); the sweep operation uses
// the same value under the name ZOUTS.
static const int MIN_ZOUTS = 7;
extern string randomSietchZaddr();
@@ -47,24 +52,8 @@ void AsyncRPCOperation_saplingconsolidation::main() {
try {
success = main_impl();
} catch (const UniValue& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
set_error_from_current_exception();
}
stop_execution_clock();
@@ -107,8 +96,18 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() {
auto opid=getId();
LogPrintf("%s: Beginning AsyncRPCOperation_saplingconsolidation\n", opid);
auto consensusParams = Params().GetConsensus();
auto nextActivationHeight = NextActivationHeight(targetHeight_, consensusParams);
if (nextActivationHeight && targetHeight_ + CONSOLIDATION_EXPIRY_DELTA >= nextActivationHeight.get()) {
int tipHeight;
{
LOCK(cs_main);
tipHeight = (chainActive.Tip() != NULL) ? chainActive.Tip()->GetHeight() : targetHeight_;
}
// Build and expire against tipHeight (execution-time), not the stale
// enqueue-time targetHeight_, so the builder's consensus-branch selection and
// the NU-straddle guard agree with the height the tx is signed for. Mirrors
// the autoshield op (commit 65130c312).
auto nextActivationHeight = NextActivationHeight(tipHeight, consensusParams);
if (nextActivationHeight && tipHeight + AUTO_OP_EXPIRY_DELTA >= nextActivationHeight.get()) {
LogPrintf("%s: Consolidation txs would be created before a NU activation but may expire after. Skipping this round.\n",opid);
setConsolidationResult(0, 0, std::vector<std::string>());
return status;
@@ -189,8 +188,8 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() {
if (fromNotes.size() < minQuantity)
continue;
auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain);
builder.SetExpiryHeight(targetHeight_ + CONSOLIDATION_EXPIRY_DELTA);
auto builder = TransactionBuilder(consensusParams, tipHeight, pwalletMain);
builder.SetExpiryHeight(tipHeight + AUTO_OP_EXPIRY_DELTA);
auto actualAmountToSend = amountToSend < fConsolidationTxFee ? 0 : amountToSend - fConsolidationTxFee;
LogPrintf("%s: %s Beginning to create transaction with Sapling output amount=%s\n", __func__, opid, FormatMoney(actualAmountToSend));
@@ -230,10 +229,10 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() {
builder.AddSaplingOutput(extsk.expsk.ovk, addr, actualAmountToSend);
LogPrint("zrpcunsafe", "%s: Added consolidation output %s with amount=%li\n", opid, addr.GetHash().ToString().c_str(), actualAmountToSend);
// Add sietch zouts
int MIN_ZOUTS = 7;
// Add sietch zouts: MIN_ZOUTS dummy zero-value shielded outputs to
// randomly-generated z-addresses, so the consolidation tx does not
// shrink the anonymity set.
for(size_t i = 0; i < MIN_ZOUTS; i++) {
// In Privacy Zdust We Trust -- Duke
string zdust = randomSietchZaddr();
auto zaddr = DecodePaymentAddress(zdust);
if (IsValidPaymentAddress(zaddr)) {
@@ -305,6 +304,13 @@ void AsyncRPCOperation_saplingconsolidation::setConsolidationResult(int numTxCre
}
void AsyncRPCOperation_saplingconsolidation::cancel() {
// Cancelling is how the scheduler stops an in-flight round, so unlike the base
// class this must be able to move an EXECUTING operation to CANCELLED. What it
// must not do is overwrite a state that is already terminal: the scheduler
// cancels the previous operation when it enqueues the next one, and that one may
// have already SUCCEEDED, whose result would otherwise be relabelled as cancelled.
if (isSuccess() || isFailed() || isCancelled())
return;
set_state(OperationStatus::CANCELLED);
}

View File

@@ -155,23 +155,8 @@ void AsyncRPCOperation_sendmany::main() {
try {
success = main_impl();
} catch (const UniValue& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
set_error_from_current_exception();
}
unlock_notes();
@@ -215,9 +200,10 @@ bool AsyncRPCOperation_sendmany::main_impl() {
bool isPureTaddrOnlyTx = (isfromtaddr_ && z_outputs_.size() == 0);
CAmount minersFee = fee_;
// TODO: fix this garbage ZEC prisoner mindset bullshit
// When spending coinbase utxos, you can only specify a single zaddr as the change must go somewhere
// and if there are multiple zaddrs, we don't know where to send it.
// Coinbase-change routing constraint:
// When spending coinbase UTXOs, only a single zaddr recipient may be specified, because the
// change must be routed somewhere and with multiple zaddr recipients there is no unambiguous
// destination for it. See the isSingleZaddrOutput / isMultipleZaddrOutput handling below.
if (isfromtaddr_) {
if (isSingleZaddrOutput) {
bool b = find_utxos(true);
@@ -325,14 +311,12 @@ bool AsyncRPCOperation_sendmany::main_impl() {
CScript scriptPubKey;
for (auto t : t_inputs_) {
scriptPubKey = GetScriptForDestination(std::get<4>(t));
//printf("Checking new script: %s\n", scriptPubKey.ToString().c_str());
uint256 txid = std::get<0>(t);
int vout = std::get<1>(t);
CAmount amount = std::get<2>(t);
builder_.AddTransparentInput(COutPoint(txid, vout), scriptPubKey, amount);
}
// for other chains, set locktime to spend time locked coinbases
//builder_.SetLockTime((uint32_t)chainActive.Tip()->GetMedianTimePast());
} else {
CMutableTransaction rawTx(tx_);
for (SendManyInputUTXO & t : t_inputs_) {
@@ -342,7 +326,6 @@ bool AsyncRPCOperation_sendmany::main_impl() {
CTxIn in(COutPoint(txid, vout));
rawTx.vin.push_back(in);
}
//rawTx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast();
tx_ = CTransaction(rawTx);
}
}
@@ -357,8 +340,8 @@ bool AsyncRPCOperation_sendmany::main_impl() {
/**
* SCENARIO #0 (All HUSH and Hush Arrakis Chains)
* Sprout not involved, so we just use the TransactionBuilder and we're done.
* SCENARIO #0 (DragonX and all Sapling-only chains)
* Sprout is not involved, so we just use the TransactionBuilder and we're done.
* We added the transparent inputs to the builder earlier.
*/
if (isUsingBuilder_) {
@@ -416,7 +399,6 @@ bool AsyncRPCOperation_sendmany::main_impl() {
}
// Fetch Sapling anchor and witnesses
//LogPrintf("%s: Gathering anchors and witnesses\n", __FUNCTION__);
uint256 anchor;
std::vector<boost::optional<SaplingWitness>> witnesses;
{
@@ -510,7 +492,8 @@ bool AsyncRPCOperation_sendmany::main_impl() {
return true;
}
// END SCENARIO #0
// No other scenarios, because Hush developers are elite.
// No other scenarios: DragonX is Sapling-only (Sprout removed), so the builder path above
// handles every supported case. Reaching here means the builder was not used, which is unexpected.
return false;
}
@@ -625,7 +608,6 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
continue;
}
//printf("%s\n", boost::apply_visitor(AddressVisitorString(), dest).c_str());
if (!destinations.count(dest)) {
continue;
}
@@ -673,7 +655,8 @@ void AsyncRPCOperation_sendmany::add_taddr_outputs_to_tx() {
rawTx.vout.push_back(out);
}
if ( !hush_hardfork_active((uint32_t)chainActive.LastTip()->nTime) )
rawTx.nLockTime = (uint32_t)time(NULL) - 60; // jl777
// Pre-hardfork: set nLockTime slightly in the past so the tx is immediately spendable.
rawTx.nLockTime = (uint32_t)time(NULL) - 60;
else
rawTx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast();
@@ -703,7 +686,8 @@ void AsyncRPCOperation_sendmany::add_taddr_change_output_to_tx(CBitcoinAddress *
CMutableTransaction rawTx(tx_);
rawTx.vout.push_back(out);
if ( !hush_hardfork_active((uint32_t)chainActive.LastTip()->nTime) )
rawTx.nLockTime = (uint32_t)time(NULL) - 60; // jl777
// Pre-hardfork: set nLockTime slightly in the past so the tx is immediately spendable.
rawTx.nLockTime = (uint32_t)time(NULL) - 60;
else
rawTx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast();
tx_ = CTransaction(rawTx);

View File

@@ -68,7 +68,7 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Empty inputs");
}
if (donation < 0 || donation > 10 ) {
if (donation > 10 ) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid donation percentage, must be an integer between 0 and 10 inclusive");
}
@@ -116,23 +116,8 @@ void AsyncRPCOperation_shieldcoinbase::main() {
try {
success = main_impl();
} catch (const UniValue& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
set_error_from_current_exception();
}
#ifdef ENABLE_MINING

View File

@@ -19,7 +19,6 @@ extern string randomSietchZaddr();
CAmount fSweepTxFee = DEFAULT_SWEEP_FEE;
bool fSweepMapUsed = false;
const int SWEEP_EXPIRY_DELTA = 15;
boost::optional<libzcash::SaplingPaymentAddress> rpcSweepAddress;
AsyncRPCOperation_sweep::AsyncRPCOperation_sweep(int targetHeight, bool fromRpc) : targetHeight_(targetHeight), fromRPC_(fromRpc){}
@@ -46,23 +45,8 @@ void AsyncRPCOperation_sweep::main() {
try {
success = main_impl();
} catch (const UniValue& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
set_error_from_current_exception();
}
stop_execution_clock();
@@ -112,7 +96,7 @@ bool IsExcludedAddress(libzcash::SaplingPaymentAddress zaddr) {
}
} else {
// This is an invalid sapling zaddr
LogPrintf("%s: Invalid zsweepexclude zaddr %s, ignoring\n", sweepExcludeAddress);
LogPrintf("%s: Invalid zsweepexclude zaddr %s, ignoring\n", __func__, sweepExcludeAddress);
continue;
}
@@ -126,10 +110,21 @@ bool AsyncRPCOperation_sweep::main_impl() {
auto opid=getId();
LogPrintf("%s: Beginning asyncrpcoperation_sweep.\n", getId());
auto consensusParams = Params().GetConsensus();
auto nextActivationHeight = NextActivationHeight(targetHeight_, consensusParams);
if (nextActivationHeight && targetHeight_ + SWEEP_EXPIRY_DELTA >= nextActivationHeight.get()) {
int tipHeight;
{
LOCK(cs_main);
tipHeight = (chainActive.Tip() != NULL) ? chainActive.Tip()->GetHeight() : targetHeight_;
}
// Key the NU-straddle guard and the tx builder/expiry off tipHeight (the
// height we actually build and expire against), not the stale enqueue-time
// targetHeight_, so a queue delay cannot slip a straddling expiry past this
// guard. Mirrors the autoshield op (commit 65130c312).
auto nextActivationHeight = NextActivationHeight(tipHeight, consensusParams);
if (nextActivationHeight && tipHeight + AUTO_OP_EXPIRY_DELTA >= nextActivationHeight.get()) {
LogPrintf("%s: Sweep txs would be created before a NU activation but may expire after. Skipping this round.\n", getId());
setSweepResult(0, 0, std::vector<std::string>());
sweepComplete_ = true; // nothing to do this round; back nextSweep off one interval instead of re-dispatching every block
return true;
}
@@ -258,11 +253,8 @@ bool AsyncRPCOperation_sweep::main_impl() {
fee = 0;
}
auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain);
{
LOCK2(cs_main, pwalletMain->cs_wallet);
builder.SetExpiryHeight(chainActive.Tip()->GetHeight()+ SWEEP_EXPIRY_DELTA);
}
auto builder = TransactionBuilder(consensusParams, tipHeight, pwalletMain);
builder.SetExpiryHeight(tipHeight + AUTO_OP_EXPIRY_DELTA);
LogPrintf("%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - fee));
// Select Sapling notes
@@ -364,6 +356,13 @@ void AsyncRPCOperation_sweep::setSweepResult(int numTxCreated, const CAmount& am
}
void AsyncRPCOperation_sweep::cancel() {
// Cancelling is how the scheduler stops an in-flight round, so unlike the base
// class this must be able to move an EXECUTING operation to CANCELLED. What it
// must not do is overwrite a state that is already terminal: the scheduler
// cancels the previous operation when it enqueues the next one, and that one may
// have already SUCCEEDED, whose result would otherwise be relabelled as cancelled.
if (isSuccess() || isFailed() || isCancelled())
return;
set_state(OperationStatus::CANCELLED);
}

Some files were not shown because too many files have changed in this diff Show More