diff --git a/src/impl/dgb/peer.hpp b/src/impl/dgb/peer.hpp new file mode 100644 index 000000000..91915040b --- /dev/null +++ b/src/impl/dgb/peer.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "coin/transaction.hpp" + +#include +#include +#include +#include + +namespace dgb +{ + +struct Peer +{ + std::optional m_other_version; + std::string m_other_subversion; + uint64_t m_other_services; + uint64_t m_nonce; + std::chrono::steady_clock::time_point m_connected_at{std::chrono::steady_clock::now()}; + + std::set m_remote_txs; // hashes + // int32_t remote_remembered_txs_size = 0; + + std::map m_remembered_txs; + // int32_t remembered_txs_size = 0; + // const int32_t max_remembered_txs_size = 25000000; +}; + +}; // namespace dgb \ No newline at end of file diff --git a/src/impl/dgb/share.hpp b/src/impl/dgb/share.hpp new file mode 100644 index 000000000..d933a4812 --- /dev/null +++ b/src/impl/dgb/share.hpp @@ -0,0 +1,316 @@ +#pragma once + +#include "coin/block.hpp" +#include "share_types.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace dgb +{ + +// min_header [small_block_header_type] +// +// share_info_type: +// share_data: +// prev_hash +// coinbase +// nonce +// address[>=34] or pubkey_hash[<34] +// subsidy +// donation +// stale_info +// desired_version +// +// segwit_data [if segwit_activated] +// +// if version < 34: +// new_transaction_hashes +// transaction_hash_refs +// +// far_share_hash +// max_bits +// bits +// timestamp +// absheight +// abswork +// +// ref_merkle_link +// last_txout_nonce +// hash_link +// merkle_link + +template +struct BaseShare : chain::BaseShare +{ + coin::SmallBlockHeaderType m_min_header; + // prev_hash + BaseScript m_coinbase; // coinbase + uint32_t m_nonce; // nonce + // [x]address[>=34] or [x]pubkey_hash[<34] + uint64_t m_subsidy; // subsidy + uint16_t m_donation; // donation + dgb::StaleInfo m_stale_info; // stale_info + uint64_t m_desired_version; // desired_version + // + // [x] segwit_data [if segwit_activated] + // + uint256 m_far_share_hash; // far_share_hash + uint32_t m_max_bits; // max_bits; bitcoin_data.FloatingIntegerType() + uint32_t m_bits; // bits; bitcoin_data.FloatingIntegerType() + uint32_t m_timestamp; // timestamp + uint32_t m_absheight; // absheight + uint128 m_abswork; // abswork + + // ref_merkle_link + MerkleLink m_ref_merkle_link; + // last_txout_nonce + uint64_t m_last_txout_nonce; + // hash_link + HashLinkType m_hash_link; + // merkle_link + MerkleLink m_merkle_link; + + NetService peer_addr; // WHERE? + + BaseShare() {} + BaseShare(const uint256& hash, const uint256& prev_hash) : chain::BaseShare(hash, prev_hash) {} + +}; + +struct Share : BaseShare<17> +{ + uint160 m_pubkey_hash; + std::optional m_segwit_data; + dgb::ShareTxInfo m_tx_info; // new_transaction_hashes; transaction_hash_refs + + Share() {} + Share(const uint256& hash, const uint256& prev_hash) : BaseShare<17>(hash, prev_hash) {} + +}; + +struct NewShare : BaseShare<33> +{ + uint160 m_pubkey_hash; + std::optional m_segwit_data; + dgb::ShareTxInfo m_tx_info; // new_transaction_hashes; transaction_hash_refs + + NewShare() {} + NewShare(const uint256& hash, const uint256& prev_hash) : BaseShare<33>(hash, prev_hash) {} +}; + +namespace types +{ + +struct DataSegwitShare +{ + BaseScript m_address; // Todo (check): VarStrType + std::optional m_segwit_data; +}; + +} // namespace types + +struct SegwitMiningShare : BaseShare<34>, types::DataSegwitShare +{ + SegwitMiningShare() {} + SegwitMiningShare(const uint256& hash, const uint256& prev_hash) : BaseShare<34>(hash, prev_hash) {} +}; + +struct PaddingBugfixShare : BaseShare<35>, types::DataSegwitShare +{ + PaddingBugfixShare() {} + PaddingBugfixShare(const uint256& hash, const uint256& prev_hash) : BaseShare<35>(hash, prev_hash) {} +}; + +struct MergedMiningShare : BaseShare<36> +{ + uint160 m_pubkey_hash; + uint8_t m_pubkey_type{0}; // 0=P2PKH, 1=P2WPKH, 2=P2SH + std::optional m_segwit_data; + std::vector m_merged_addresses; // empty = none + std::vector m_merged_coinbase_info; // empty = none + uint256 m_merged_payout_hash; // zero = none + V36HashLinkType m_hash_link; // shadows BaseShare::m_hash_link + BaseScript m_message_data; // empty = none + + MergedMiningShare() {} + MergedMiningShare(const uint256& hash, const uint256& prev_hash) : BaseShare<36>(hash, prev_hash) {} +}; + +struct Formatter +{ + SHARE_FORMATTER() + { + // small_block_header_type: + READWRITE(obj->m_min_header); + // share_info_type: + READWRITE( + obj->m_prev_hash, + obj->m_coinbase, + obj->m_nonce + ); + + // Address handling — version-dependent + if constexpr (version >= 36) + { + READWRITE(obj->m_pubkey_hash); // IntType(160) + READWRITE(obj->m_pubkey_type); // IntType(8) + } + else if constexpr (version >= 34) + { + READWRITE(obj->m_address); + } + else + { + READWRITE(obj->m_pubkey_hash); // pubkey_hash + } + + // Subsidy — V36 uses VarInt, others use fixed uint64 + if constexpr (version >= 36) + { + READWRITE(VarInt(obj->m_subsidy)); + } + else + { + READWRITE(obj->m_subsidy); + } + + READWRITE( + obj->m_donation, + Using>>(obj->m_stale_info), + VarInt(obj->m_desired_version) + ); + + if constexpr (is_segwit_activated(version)) + { + READWRITE(Optional(obj->m_segwit_data, SegwitDataDefault)); + } + + // V36: merged_addresses (after segwit_data, before far_share_hash) + if constexpr (version >= 36) + { + READWRITE(obj->m_merged_addresses); + } + + if constexpr (version < 34) + { + READWRITE(obj->m_tx_info); + } + + READWRITE( + obj->m_far_share_hash, + obj->m_max_bits, + obj->m_bits, + obj->m_timestamp, + obj->m_absheight + ); + + // Abswork — V36 uses VarInt-encoded uint64, others use fixed uint128 + if constexpr (version >= 36) + { + READWRITE(Using(obj->m_abswork)); + } + else + { + READWRITE(obj->m_abswork); + } + + // V36: merged_coinbase_info + merged_payout_hash (after abswork) + if constexpr (version >= 36) + { + READWRITE(obj->m_merged_coinbase_info); + READWRITE(obj->m_merged_payout_hash); + } + + // ref_merkle_link + READWRITE( + MERKLE_LINK_SMALL(obj->m_ref_merkle_link) + ); + // last_txout_nonce + READWRITE(obj->m_last_txout_nonce); + // hash_link (V36: V36HashLinkType with extra_data; others: HashLinkType) + READWRITE(obj->m_hash_link); + // merkle_link + READWRITE( + MERKLE_LINK_SMALL(obj->m_merkle_link) + ); + + // V36: message_data (at the end) + if constexpr (version >= 36) + { + READWRITE(obj->m_message_data); + } + } +}; + +using ShareType = chain::ShareVariants; + +inline ShareType load_share(chain::RawShare& rshare, NetService peer_addr) +{ + auto stream = rshare.contents.as_stream(); + auto share = ShareType::load(rshare.type, stream); + share.ACTION({ obj->peer_addr = peer_addr; }); + return share; +} + +template +inline ShareType load_share(int64_t version, StreamType& is, NetService peer_addr) +{ + auto share = ShareType::load(version, is); + share.ACTION({ obj->peer_addr = peer_addr; }); + return share; +} + +struct ShareHasher +{ + // this used to call `GetCheapHash()` in uint256, which was later moved; the + // cheap hash function simply calls ReadLE64() however, so the end result is + // identical + size_t operator()(const uint256& hash) const + { + return hash.GetLow64(); + } +}; + +class ShareIndex : public chain::ShareIndex +{ + using base_index = chain::ShareIndex; + +public: + // Per-share fields (NOT accumulated — each share stores only its own values). + // Accumulated values are computed on-the-fly by walking m_shares[tail]. + uint288 work; // target_to_average_attempts(bits) + uint288 min_work; // target_to_average_attempts(max_bits) + + // Per-share metadata + int64_t time_seen{0}; + int32_t naughty{0}; + bool is_block_solution{false}; // pow_hash <= block_target (set during init_verify) + uint256 pow_hash; // scrypt hash, cached at reception (for block scan) + + ShareIndex() : base_index(), work(0), min_work(0) {} + + template ShareIndex(ShareT* share) : base_index(share) + { + work = chain::target_to_average_attempts(chain::bits_to_target(share->m_bits)); + min_work = chain::target_to_average_attempts(chain::bits_to_target(share->m_max_bits)); + time_seen = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } +}; + +struct ShareChain : chain::ShareChain +{ + +}; + +} // namespace dgb + diff --git a/src/impl/dgb/share_types.hpp b/src/impl/dgb/share_types.hpp new file mode 100644 index 000000000..ef09f8e7b --- /dev/null +++ b/src/impl/dgb/share_types.hpp @@ -0,0 +1,196 @@ +#pragma once + +#include +#include +#include + +namespace dgb +{ + +const uint64_t SEGWIT_ACTIVATION_VERSION = 17; + +constexpr bool is_segwit_activated(uint64_t version) +{ + return version >= SEGWIT_ACTIVATION_VERSION; +} + +enum StaleInfo +{ + none = 0, + orphan = 253, + doa = 254 +}; + +struct MerkleLinkParams +{ + const bool allow_index; + + SER_PARAMS_OPFUNC +}; + +constexpr static MerkleLinkParams MERKLE_LINK_SMALL {.allow_index = false}; +constexpr static MerkleLinkParams MERKLE_LINK_FULL {.allow_index = true}; + +struct MerkleLink +{ + std::vector m_branch; + uint32_t m_index{0}; + + MerkleLink() { } + + template + void UnserializeMerkleLink(StreamType& s, const MerkleLinkParams& params) + { + s >> m_branch; + if (params.allow_index) + s >> m_index; + } + + template + void SerializeMerkleLink(StreamType& s, const MerkleLinkParams& params) const + { + s << m_branch; + if (params.allow_index) + s << m_index; + } + + template + inline void Serialize(StreamType& os) const + { + SerializeMerkleLink(os, os.GetParams()); + } + + template + inline void Unserialize(StreamType& is) + { + UnserializeMerkleLink(is, is.GetParams()); + } +}; + +struct SegwitData +{ + MerkleLink m_txid_merkle_link; + uint256 m_wtxid_merkle_root; + + SegwitData() {} + SegwitData(MerkleLink txid_merkle_link, uint256 wtxid) : m_txid_merkle_link(txid_merkle_link), m_wtxid_merkle_root(wtxid) { } + + C2POOL_SERIALIZE_METHODS(SegwitData) { READWRITE(MERKLE_LINK_SMALL(obj.m_txid_merkle_link), obj.m_wtxid_merkle_root); } +}; + +struct SegwitDataDefault +{ + static SegwitData get() + { + // Sentinel must match p2pool's PossiblyNoneType sentinel: + // dict(txid_merkle_link=dict(branch=[], index=0), wtxid_merkle_root=0) + // Using all-0xff caused p2pool to interpret "None" as a valid wtxid root, + // producing different witness commitment → different coinbase txid. + return SegwitData{{}, uint256()}; // zero = None sentinel + } +}; + +struct TxHashRefs +{ + uint64_t m_share_count; + uint64_t m_tx_count; + + TxHashRefs() = default; + TxHashRefs(uint64_t share, uint64_t tx) : m_share_count(share), m_tx_count(tx) {} + + C2POOL_SERIALIZE_METHODS(TxHashRefs) { READWRITE(VarInt(obj.m_share_count), VarInt(obj.m_tx_count)); } +}; + +struct ShareTxInfo +{ + std::vector m_new_transaction_hashes; + std::vector m_transaction_hash_refs; //pack.ListType(pack.VarIntType(), 2)), # pairs of share_count, tx_count + + ShareTxInfo() = default; + + ShareTxInfo(const auto& new_tx_hashes, const auto& tx_hash_refs) + : m_new_transaction_hashes(new_tx_hashes), m_transaction_hash_refs(tx_hash_refs) { } + + C2POOL_SERIALIZE_METHODS(ShareTxInfo) { READWRITE(obj.m_new_transaction_hashes, obj.m_transaction_hash_refs); } +}; + +struct HashLinkType +{ + FixedStrType<32> m_state; //pack.FixedStrType(32) + // FixedStrType<0> m_extra_data; //pack.FixedStrType(0) # bit of a hack, but since the donation script is at the end, const_ending is long enough to always make this empty + uint64_t m_length; //pack.VarIntType() + + C2POOL_SERIALIZE_METHODS(HashLinkType) { READWRITE(obj.m_state, /*obj.m_extra_data,*/ VarInt(obj.m_length)); } +}; + +// V36 hash link (DGB-Scrypt; share format parity with p2pool-merged-v36) — extra_data becomes VarStr (was FixedStr(0) pre-V36) +struct V36HashLinkType +{ + FixedStrType<32> m_state; + BaseScript m_extra_data; // VarStr in V36 + uint64_t m_length; + + C2POOL_SERIALIZE_METHODS(V36HashLinkType) { READWRITE(obj.m_state, obj.m_extra_data, VarInt(obj.m_length)); } +}; + +// V36 merged mining: per-chain address entry +struct MergedAddressEntry +{ + uint32_t m_chain_id; + BaseScript m_script; + + C2POOL_SERIALIZE_METHODS(MergedAddressEntry) { READWRITE(obj.m_chain_id, obj.m_script); } +}; + +// V36 merged mining: per-chain coinbase verification entry +struct MergedCoinbaseEntry +{ + uint32_t m_chain_id; + uint64_t m_coinbase_value; + uint32_t m_block_height; + FixedStrType<80> m_block_header; + MerkleLink m_coinbase_merkle_link; + BaseScript m_coinbase_script; // V36: actual scriptSig (allows custom tags + THE state_root) + + template + void Serialize(StreamType& os) const + { + ::Serialize(os, m_chain_id); + ::Serialize(os, Using(m_coinbase_value)); + ::Serialize(os, Using(m_block_height)); + ::Serialize(os, m_block_header); + ParamPackStream pstream{MERKLE_LINK_SMALL, os}; + ::Serialize(pstream, m_coinbase_merkle_link); + ::Serialize(os, m_coinbase_script); + } + + template + void Unserialize(StreamType& is) + { + ::Unserialize(is, m_chain_id); + ::Unserialize(is, Using(m_coinbase_value)); + ::Unserialize(is, Using(m_block_height)); + ::Unserialize(is, m_block_header); + ParamPackStream pstream{MERKLE_LINK_SMALL, is}; + ::Unserialize(pstream, m_coinbase_merkle_link); + ::Unserialize(is, m_coinbase_script); + } +}; + +// V36: abswork is VarInt-encoded on the wire but stored as uint128 +struct AbsworkV36Format +{ + template + static void Write(StreamType& os, const uint128& value) + { + WriteCompactSize(os, value.GetLow64()); + } + + template + static void Read(StreamType& is, uint128& value) + { + value = uint128(ReadCompactSize(is, false)); + } +}; + +} // namespace dgb