Skip to content

bch: M3 coin_node embedded SPV+P2P layer (slices 1-13, source-only) - #106

Merged
frstrtr merged 18 commits into
masterfrom
bch/m3-coin-node
Jun 16, 2026
Merged

bch: M3 coin_node embedded SPV+P2P layer (slices 1-13, source-only)#106
frstrtr merged 18 commits into
masterfrom
bch/m3-coin-node

Conversation

@frstrtr

@frstrtr frstrtr commented Jun 16, 2026

Copy link
Copy Markdown
Owner

M3 BCH embedded coin-node front-end: substrate (slices 1-5) + full P2P layer (slices 6-13), ported from the btc reference into src/impl/bch/coin/ only.

Slice 13 (last P2P file) lands node.hpp -- the template Node over NodeRPC + NodeP2P<config_t>; config binds only at the concrete Node instantiation (binary entrypoint), mirroring the btc deferral. Config interleave is byte-identical to btc and stays dependent under the template -- no prefix/p2p-port divergence.

Isolation: single-coin-tree (src/impl/bch/coin/ only). impl_bch stays UNREGISTERED in CMake (bch = skip-green) so CI is untouched. Validation per slice: standalone parse gate g++ -fsyntax-only -std=c++20, exit 0.

BCH divergences (standalone SHA256d parent): no MWEB, no AuxPoW hooks, cmpct v1, 70016 handshake (no NODE_WITNESS/wtxidrelay), DSPROOF inv recognized-but-not-requested.

All commits GPG-signed. Proposes only -- operator owns the merge.

frstrtr added 18 commits June 15, 2026 20:23
First source-only slice of the M3 coin_node port. Mirrors
src/impl/btc/coin/{rpc_data,transaction}.{hpp,cpp} under the bch::coin
namespace, matching the core::coin::ICoinNode seam convention
(<coin>::coin::CoinNode).

BCH divergence (M1 §4.1): no SegWit. Transaction uses the legacy
version/vin/vout/locktime serialization only -- TxParams{allow_witness},
TxIn::scriptWitness, the dummy/flags extended-format branch, and
HasWitness()/ComputeHasWitness() are all dropped. BCH txids derive from
this single canonical serialization. CashTokens round-trip transparently
through OPScript (token-aware template handling is a later slice).

rpc_data WorkData omits the LTC mweb field (N/A for BCH). Coin-agnostic
slice (m_data/m_hashes/m_latency) is unchanged for the WorkView seam.

Not yet wired into CMake (impl_bch unregistered) -- source-only so it
cannot perturb the shared build.
Continues the M3 coin_node port chain (rpc_data -> transaction -> block ->
txidcache -> node_interface -> coin_node). Models src/impl/btc/coin/{block,
txidcache}.hpp into the bch:: namespace under the core::coin::ICoinNode seam.

BCH divergences (M1 sec.4.1):
  * block.hpp: NO SegWit -- the block tx vector serializes with the plain
    legacy encoding; the BTC source wrapped it in TX_WITH_WITNESS(m_txs),
    dropped here. The 80-byte header is structurally identical to BTC; ASERT
    DAA (Nov 2020) changes how m_bits is computed, not the header layout.
  * CTOR (Nov 2018): canonical ascending-txid ordering of non-coinbase txs is
    a consensus rule enforced by the template builder / validation path, not by
    BlockType -- the struct stores txs as given. Ordering insertion point is
    coin/template_builder.hpp (a later slice).
  * txidcache.hpp: coin-agnostic, ported verbatim. CashTokens data rides inside
    the packed tx and hashes transparently; CTOR does not affect cache keying.

Source-only: impl_bch stays unregistered in CMake, so this cannot perturb the
shared build. Both headers pass g++ -std=c++20 -fsyntax-only -I src.
… slice 3)

node_interface.hpp -> bch::interfaces::Node: aggregates the slice 1-2 ports
(rpc::WorkData, Transaction, BlockHeaderType, BlockType, TXIDCache). full_block
carries plain BlockType -- no MWEB extension (LTC-specific; absent on BCH).

template_builder.hpp: add the abstract CoinNodeInterface (work-source/submit
seam) co-located with the builder as in the BTC source. Depends only on
rpc::WorkData + BlockType, so it lands now, ahead of the M4 TemplateBuilder
body (GBT assembly + CTOR re-sort + CashTokens carry, left as TODO).

Source-only; impl_bch stays unregistered in CMake (bch = skip-green) so CI is
untouched. Both headers pass -fsyntax-only against the slice 1-2 tree.
rpc.hpp: NodeRPC declaration ported from src/impl/btc/coin/rpc.hpp so the
concrete seam can name NodeRPC* and call getwork()/submit_block_hex(). The
network BODY (rpc.cpp) is deferred to the next slice -- it carries the
BCH-divergent bits with no 1:1 BTC mirror (getblocktemplate has NO segwit
rule; softfork activations are ASERT/CTOR/CashTokens/ABLA, not BIP9) and
depends on config_pool.hpp + softfork_check.hpp, neither yet ported.

coin_node.{hpp,cpp}: concrete bch::coin::CoinNode : core::coin::ICoinNode,
1:1 mirror of btc::coin::CoinNode. get_work_view() keeps full WorkData
coin-side via work.set(wd) and hands core only the agnostic slice;
submit_block_hex() forwards the 2-arg/no-mweb form (MWEB is LTC-specific).

Build-INERT: defines the type, not yet wired into web_server. impl_bch stays
unregistered in CMake (bch = skip-green) so CI is untouched. All three pass
-fsyntax-only against the slice 1-3 tree.
Completes the M3 coin_node port. Lands the BCH-divergent NodeRPC network
body and its two prerequisites (config_pool, softfork_check).

config_pool.hpp/.cpp: BCH PoolConfig with all network constants reauthored
from the p2pool-merged-v36 BCH reference (bitcoincash.py + bitcoin/networks/
bitcoincash.py) for V36 master-compat: P2P_PORT 9349, IDENTIFIER/PREFIX,
SHARE_PERIOD 60s, CHAIN_LENGTH 4320, MIN_PROTO 3301, BLOCK_MAX_SIZE 32MB,
DUST 100000, MAX_TARGET bdiff-1. SOFTFORKS_REQUIRED is EMPTY (BCH
activations are MTP-based, not BIP9/GBT-signalled). Donation is the single
static forrestv P2PK for all share versions (no V36 COMBINED ratchet;
verified vs p2poolBCH @6603b79).

softfork_check.hpp: collect_softfork_names ported into bch::coin; vacuous
gate on BCH (empty required set) kept for parity + diagnostics.

rpc.cpp: check() uses the Aug-2017 fork block as the BCH-chain sentinel and
the version-100000 floor; getwork() requests getblocktemplate() with NO
segwit rule and unpacks transactions via the legacy witness-free path (no
wtxid distinction); submit_block() packs the legacy block (no witness, no
MWEB tail). CashTokens outputs round-trip transparently.

Source-only; impl_bch stays unregistered in CMake (bch = skip-green). All
four files pass -fsyntax-only -std=c++20.
Opens the M3 embedded P2P node layer. chain_seeds.hpp ports the btc/coin/
analog into bch::coin with the BCH-operated DNS seed set and BCHN default
ports, sourced from bitcoin-cash-node/src/chainparams.cpp:

  mainnet  (8333):  flowee/bitcoinforks/bitcoinunlimited/bchd/loping/
                    electroncash/c3-soft/bitjson
  testnet3 (18333): bchd/loping/bitcoinunlimited
  testnet4 (28333): toom.im/loping/flowee/bitjson

BCH inherits BTC mainnet/testnet3 ports by fork but the seed hostnames are
entirely disjoint; testnet4 is 28333 (BCHN), NOT the BTC 48333. No
trailing-dot convention (BCHN stores bare hostnames). Fixed fallbacks are a
dual DNS+node subset per network.

Source-only; impl_bch stays unregistered in CMake (bch = skip-green).
Passes -fsyntax-only -std=c++20.
Port the BTC P2P wire-message set into bch::coin::p2p with the SegWit/
BIP-339 surface removed: no MSG_WTX, no witness inventory flag, no
wtxidrelay, and tx payloads serialized witness-free. Add the BCH-specific
MSG_DOUBLESPENDPROOF (0x94a0) inventory type. The BIP-152 compact-block
carrier messages (cmpctblock/getblocktxn/blocktxn) are deferred to the
compact_blocks slice; sendcmpct negotiation is kept (no carrier dep).
Source-only; impl_bch stays unregistered in CMake (skip-green).
…M3 slice 8)

Port the embedded mempool into bch::coin. BCH divergences from the
BTC/LTC source: drop BIP141 weight/witness_size in favour of raw
byte_size (feerate is sat/byte); txid = SHA256d of the single
witness-free serialization with wtxid == txid; all_txs_map_wtxid()
aliases all_txs_map() for the compact-block call site. Local
BCH_LIMITS (21M sat, 100-block maturity, no pegout) keeps the module
off the shared core path. Unblocks the compact_blocks leaf.
…ort-IDs (M3 slice 9)

BIP152 HeaderAndShortIDs + getblocktxn/blocktxn carriers ported from btc.
BCH divergences: legacy (no-witness) tx bodies (2-arg UnserializeTransaction,
no TX_WITH_WITNESS); short IDs computed over txid via compute_txid (wtxid==txid);
reconstruction keyed by caller mempool.all_txs_map_wtxid() alias. CTOR ordering
left to template-builder/validation, not enforced in carriers. Source-only; not
yet wired into p2p_messages (cmpct-wireback is the next slice).
… slice 10)

cmpct-wireback. Add the 3 BIP152 carrier message types -- cmpctblock
(HeaderAndShortIDs), getblocktxn (BlockTransactionsRequest), blocktxn
(BlockTransactionsResponse) -- wrapping the types from compact_blocks.hpp
(slice 9), and register them in the p2p Handler.

The BCH divergences from UID850 (no-witness tx bodies via the 2-arg
UnserializeTransaction, short IDs over txid via compute_txid with the
wtxid==txid alias, no CTOR re-sort) all live in the wrapped compact_blocks
types, so the carrier wiring is structurally identical to btc UID850 --
with one exception: NO wtxidrelay carrier is added, since BCH has no wtxid.

sendcmpct (present since slice 7) is unchanged. Source-only; impl_bch
stays unregistered (bch=skip-green), off ci-steward OBJECT-lib PR lane.
Kept out of the 06-18 substrate PR (slices 1-5).

Syntax-gated: g++ -std=c++20 -fsyntax-only clean.
…ce 11)

Structural mirror of btc p2p_connection: ReplyMatcher-backed get_block /
get_header request pairing plus guarded socket write path. No BCH-specific
logic here — the wire divergences (no-witness serialization, txid-based
short IDs, wtxid==txid) stay in p2p_messages + compact_blocks, and version
handshake / wtxidrelay omission belongs to p2p_node. Source-only; impl_bch
remains unregistered (bch=skip-green). Syntax-gated clean under
g++ -std=c++20 -fsyntax-only.
Port the 38KB NodeP2P driver from btc UID: handshake, header/block sync,
inv relay, BIP 152 compact blocks, and the BIP 35/130/133 niceties. The
class is templated on ConfigType and is structurally a mirror of the btc
reference; the BCH divergences are:

  * NO SegWit. version advertises NODE_NETWORK | NODE_BITCOIN_CASH (1<<5),
    never NODE_WITNESS. inv getdata uses plain MSG_TX (1) / MSG_BLOCK (2)
    -- no witness-flagged variants, no MSG_WTX path. inventory_type has no
    base_type() mask, so m_type is switched directly.
  * NO BIP 339 wtxidrelay. The wtxidrelay member, accessor and handler are
    dropped -- it is absent from the p2p_messages Handler variant too
    (wtxid == txid on BCH).
  * Compact blocks negotiate version 1 (sendcmpct false,1): short IDs keyed
    on txid, not witness-txid. submit_block_raw accepts cmpct ver >= 1. The
    cmpctblock/blocktxn reconstruction maps are txid-keyed; mempool feed via
    all_txs_map_wtxid() which aliases all_txs_map() on BCH.
  * NO AuxPoW. BCH is a standalone parent (never merged-mined), so the DOGE
    raw_headers_parser / raw_block_parser hooks are removed; headers/blocks
    use the standard SHA256d serialization.
  * doublespendproof (DSPROOF 0x94a0) inv is recognized but not requested.

Config surface: the only config touch is m_config->coin()->m_p2p.prefix via
the ConfigType template parameter -- no concrete bch config.hpp dependency.
The config binding is deferred to the NodeP2P<config_t> instantiation in
node.hpp (slice 13). Nothing is gated.

Source-only; impl_bch stays unregistered (bch=skip-green), off the
ci-steward OBJECT-lib PR lane. Syntax-gated: g++ -std=c++20 -fsyntax-only
clean.
…ce 13)

Last P2P-layer file. template<ConfigType> Node over NodeRPC + NodeP2P<config_t>,
deriving bch::interfaces::Node. Config binds only at the concrete Node<Config>
instantiation (binary entrypoint), mirroring the btc reference deferral; the
config interleave (coin()->m_p2p.address, m_rpc, m_testnet) is byte-identical
to btc and stays dependent under the template.

BCH divergences: no MWEB payload, no AuxPoW hooks, 70016 handshake (no
NODE_WITNESS/wtxidrelay). Standalone parse gate g++ -fsyntax-only exit 0;
impl_bch stays unregistered (bch=skip-green).
…M3 slice 14)

Port src/impl/btc/config.hpp into src/impl/bch/config.hpp: the coin+pool
config aggregator binding bch::PoolConfig (slice 5) and bch::CoinConfig into
the shared core::Config<PoolConfig, CoinConfig> carrier. Structurally
identical to the btc reference; all BCH divergence stays in the component
configs.

Reconcile config_coin.hpp from the M2 c2pool::bch CoinParams skeleton to
bch::CoinConfig (P2PData/RPCData structs + YAML converters + Fileconfig-
derived CoinConfig declaration), mirroring btc 1:1 so the aggregator
resolves. BCH scope notes preserved as comments: HogEx out of scope (M1 4.2,
SmartBCH sidechain construct), CashAddr encode/decode deferred to the
template/address layer (M4). The config_coin.cpp body (get_default/load)
lands next slice per the confirmed ordering.

Source-only: impl_bch stays unregistered (off the OBJECT-lib lane), bch
remains skip-green. Both headers pass g++ -std=c++20 -fsyntax-only.
…3 slice 15)

Ports src/impl/btc/config_coin.cpp to bch::CoinConfig, mirroring the slice-14
config_coin.hpp reconcile. YAML get_default/load over symbol / p2p / rpc /
share_period / testnet. Source-only; no impl_bch CMake registration (M3
ground rule, avoid racing ci-steward).

donation_consensus.hpp is HELD this slice: the dispatch directs an sv>=36
combined-P2SH donation gate, which conflicts with the verified-closed BCH
single static forrestv P2PK divergence already committed in config_pool.hpp
(get_donation_script ignores share_version by design). Surfaced to integrator
as [decision-needed] before any consensus-critical byte change.
…s (M3 slice 15)

Operator ruling 2026-06-16 (V36 master-compat with p2pool-merged-v36):
donation script is version-gated, same shape as every coin.

  share_version <  36 -> BCH-native forrestv P2PK (static), byte-for-byte
                         from p2poolBCH @6603b79 (unchanged v35 leg).
  share_version >= 36 -> COMBINED_DONATION_SCRIPT: 1-of-2 P2MS->P2SH
                         transition + AutoRatchet 95%/50% + tail-guard.
                         Byte-identical to the BTC/LTC merged-path combined
                         P2SH (coin-independent hash160) -- merged-path is
                         always COMBINED for cross-coin V36 parity.

PoolConfig::get_donation_script now returns the combined script for v36+
shares; donation_consensus.hpp consumes it (validate_donation_output /
validate_coinbase_total) so the share validator and template builder agree.
build_expected_payouts deferred to the BCH share_tracker/PPLNS port slice.
Source-only; impl_bch not yet CMake-registered (skip-green).
…ints (M3 slice 16)

Port the coin-independent PPLNS surface of ShareTracker from the LTC
reference into src/impl/bch/share_tracker.hpp:
  - PPLNS window accumulator: CumulativeWeights, PPLNSEntry,
    DensePPLNSWindow (precomputed decay table), HeadPPLNS.
  - Expected-payouts entry points: get_expected_payouts (V36 decayed),
    get_v35_expected_payouts (flat, grandparent start), plus the direct
    weight helpers get_cumulative_weights and
    get_v36_decayed_cumulative_weights.

The ported payout math and PPLNS structures are byte-identical across the
BTC and LTC references (coin-independent), reproduced verbatim so BCH
payout distribution is bit-exact with p2pool-merged-v36.

Scope: only the PPLNS/payout surface of ShareTracker lands this slice;
verify_share/think/head-scoring/version-counting are deferred to later M3
slices that extend this class. source-only: impl_bch stays
CMake-unregistered (bch skip-green).
…nt guard (M3 slice 17)

Ports the share verification + serialization surface into src/impl/bch/,
completing the dependency chain so share_tracker.hpp (slice 16) reaches
standalone syntax-check.

Source = src/impl/btc/ (NOT ltc): BCH is a standalone SHA256d parent, so its
PoW path is byte-identical to BTC. share_check.hpp differs from the LTC
reference by 232 lines that are exactly the scrypt-vs-SHA256d PoW path;
mirroring LTC would carry scrypt PoW = a p2pool-merged-v36 compat break.
share_types.hpp / share_messages.hpp are byte-identical across btc/ltc
(coin-independent); share.hpp differs from ltc only in the pow_hash comment.

share_tracker.hpp: add #include <cstdint> (mul128_shift uses uint64_t before
the include block) — build hygiene, no share-format/consensus change. Same
latent gap exists in src/impl/btc/share_tracker.hpp.

source-only: impl_bch stays CMake-unregistered (bch stays skip-green).
All five files pass standalone -fsyntax-only (g++ -std=c++20).
@frstrtr
frstrtr merged commit 423eff6 into master Jun 16, 2026
17 checks passed
frstrtr added a commit that referenced this pull request Jun 17, 2026
…r) (#120)

* bch(m3): ASERT (aserti3-2d) DAA — 1:1 BCHN pow.cpp port

Net-new BCH-specific validation slice (M1 §4.3, no btc/ analogue).
CalculateASERT + GetNextASERTWorkRequired ported byte-exact from BCHN
src/pow.cpp; anchors from chainparams.cpp (main/testnet3/testnet4).
Governs SPV header-accept target only — PoW hash stays SHA256d, zero
share-format / p2pool-merged-v36 surface change. Syntax-clean -std=c++20.

* bch(m3): header-chain validation wired to ASERT DAA (M3 slice 18b)

Port src/impl/btc/coin/header_chain.hpp to BCH: same store/sync/persistence
and SHA256d PoW, with the one BCH divergence being difficulty. Replaces
Bitcoin's 2016-block 2-week retarget with ASERT (aserti3-2d) by wiring
validate_difficulty()/get_next_work_required() to asert.hpp (slice 18a).

- BCHChainParams carries the ASERTParams anchor (asert.hpp) instead of a
  (timespan, spacing, interval) tuple; pow_limit/allow_min_difficulty mirror
  the anchor. mainnet/testnet3 genesis shared with Bitcoin (pre-fork); BCH
  testnet4 genesis flagged TODO-verify against VM300 bchn-bch chainparams.
- validate_difficulty drops the ancestor-walk lookback (ASERT is anchor-
  absolute): trusts pre-anchor history and synthetic checkpoint seeds, else
  asserts header.nBits == get_next_work_required_asert(tip,...).
- No p2pool-merged-v36 surface: ASERT governs header accept only, not the
  PoW hash or share format.

Single-coin, src-only (no impl_bch CMake registration). Syntax-clean
-std=c++20. Push held pending PR #106 ratification.

* bch: add coinbase/template commitment construction + validation

Standalone-parent commitment layout (BTC minus AuxPoW segment):
[BIP34 height][/c2pool/][state_root 32B][TheMetadata]. BIP34 enforced
per BCHN ContextualCheckBlock; no witness commitment (SegWit struck);
CTOR handled separately by template tx sort; CashTokens transparent.
Source-only header; shared by M4 GBT builder and the share validator.

* bch(m4): template builder body — GBT assembly + CTOR + ASERT (M4 slice 1)

Flesh out bch::coin::TemplateBuilder (was an M4 TODO stub) into a working
GBT assembler from HeaderChain + Mempool, mirroring the BTC builder with the
BCH consensus divergences:

  * SHA256d merkle root + 50-BCH/210k-halving subsidy (== BTC curve).
  * Next-block bits from the ASERT (aserti3-2d) DAA, not a 2016-block
    retarget; falls back to tip bits / pow_limit below the ASERT anchor.
  * CTOR (CHIP-2018-11): block body re-sorted to ascending txid after fee
    selection, coinbase excluded.
  * No SegWit: single canonical tx serialization, hash == txid, empty
    segwit-free rules array. CashTokens carried transparently in tx bytes.
  * EmbeddedCoinNode wired to build_template() for getwork().

Coinbase tx (and its s19 commitment) is still assembled downstream, as on
BTC; binding ../coinbase_commitment.hpp into the coinbase builder is the
next M4 slice. ABLA dynamic block-size and the rules array are pinned to
pre-ABLA 32 MB EB / empty here pending verification vs VM300 bchn-bch GBT
and the p2pool-merged-v36 python reference.

Source-only, -fsyntax-only clean. No impl_bch CMake registration (does not
race ci-steward); bch lane stays skip-green.

* bch(m4): ABLA block-size limit (CHIP-2023-01) + template size-budget sourcing

Add abla.hpp -- a 1:1 fixed-point port of BCHN consensus/abla.{h,cpp}
(pinned at v29.0.0): Config (MakeDefault/SetMax) + State (NextBlockState,
GetBlockSizeLimit). Mainnet grows; testnets fixed at 32 MB.

Route the template builder block-size byte budget through abla at the
activation/floor limit (32 MB) instead of a static constant. The floor is a
safe LOCAL build cap -- ABLA only ever raises it, so a template built to the
floor can never exceed the live consensus limit. Per-tip ABLA state replay
through the header chain (dynamic limit) is the next size-slice.

No p2pool-merged-v36 surface: ABLA governs only the block-size limit, not
PoW hash, share format, coinbase commitment, or PPLNS math.

Roundtrip exec: floor==32MB both nets; mainnet->41.3MB after 5000 full
blocks; testnet fixed. abla.hpp + template_builder.hpp -fsyntax-only OK.

* bch(m4): ABLA replay primitive + dynamic per-tip template size budget

Add abla::replay() -- replay ABLA State forward from a known-good anchor
over a contiguous run of full-block sizes (1:1 BCHN State::NextBlockState
loop). Wire TemplateBuilder::build_template to consume an optional caller-
supplied per-tip ABLA State, using its GetBlockSizeLimit() as the dynamic
block-size budget; absent it, fall back to the activation/floor limit
(unchanged, always-safe LOCAL build cap).

The per-block-size feed that drives replay is a full-block/embedded-daemon
concern (M5+): the headers-only SPV header_chain structurally does not carry
block sizes, so the dynamic limit is re-homed there rather than forced into
the header chain. Build budget only -- zero p2pool-merged-v36 surface
(no PoW hash, share format, coinbase commitment, or PPLNS touched).

Validated: mainnet replay 5000x32MB grows 32->39.48MB (bounded by
MAX_CONSENSUS_BLOCK_SIZE); testnet fixedSize stays 32MB; empty replay = anchor.

---------

Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
frstrtr added a commit that referenced this pull request Jun 17, 2026
Port src/impl/btc/coin/header_chain.hpp to BCH: same store/sync/persistence
and SHA256d PoW, with the one BCH divergence being difficulty. Replaces
Bitcoin's 2016-block 2-week retarget with ASERT (aserti3-2d) by wiring
validate_difficulty()/get_next_work_required() to asert.hpp (slice 18a).

- BCHChainParams carries the ASERTParams anchor (asert.hpp) instead of a
  (timespan, spacing, interval) tuple; pow_limit/allow_min_difficulty mirror
  the anchor. mainnet/testnet3 genesis shared with Bitcoin (pre-fork); BCH
  testnet4 genesis flagged TODO-verify against VM300 bchn-bch chainparams.
- validate_difficulty drops the ancestor-walk lookback (ASERT is anchor-
  absolute): trusts pre-anchor history and synthetic checkpoint seeds, else
  asserts header.nBits == get_next_work_required_asert(tip,...).
- No p2pool-merged-v36 surface: ASERT governs header accept only, not the
  PoW hash or share format.

Single-coin, src-only (no impl_bch CMake registration). Syntax-clean
-std=c++20. Push held pending PR #106 ratification.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant