feat(p2p): advertise fee policy in node_status message#943
Conversation
Adds an optional FeePolicy field to NodeStatusMessage so peers can learn each other's mining-fee rate, max script/tx size, and max sigops/tx without an out-of-band HTTP call to /v1/policy. Shape mirrors the ARC policy response (minus StandardFormatSupported, which is an ARC transport flag). Backward compatible: the field uses omitempty and the legacy min_mining_tx_fee field is unchanged, so older peers and dashboards continue to parse status messages without changes. policyFromSettings carries the BSV/kB to sat/kB conversion and bounds check, returning nil on misconfigured fees rather than aborting status publication. Closes #4639
|
🤖 Claude Code Review Status: Complete All previously identified issues have been addressed through iterative commits: History:
Current Review:
|
There was a problem hiding this comment.
Pull request overview
This PR extends the P2P node_status message to optionally include a richer fee/policy payload (fee_policy) so peers can learn each other’s mining fee rate and key policy limits without requiring an HTTP /v1/policy call.
Changes:
- Adds
FeePolicy(ARC-shaped) toNodeStatusMessageand the internalnotificationMsg, wiring it through P2P publish/receive and local WebSocket fanout. - Introduces
policyFromSettings()to build the advertised policy object from localsettings.PolicySettings. - Adds unit tests for
policyFromSettings(), JSON wire compatibility/omitempty, andgetNodeStatusMessagepopulation behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| services/p2p/Server.go | Populates and forwards the new FeePolicy field through node status publication and WebSocket notification paths. |
| services/p2p/server_handler_test.go | Adds getNodeStatusMessage tests covering FeePolicy present/absent scenarios. |
| services/p2p/message_types.go | Defines FeePolicy/FeeAmount, adds policyFromSettings(), and extends NodeStatusMessage with fee_policy. |
| services/p2p/HandleWebsocket.go | Extends WebSocket notification payload with fee_policy. |
| services/p2p/fee_policy_test.go | Adds tests for conversion logic and JSON backward/forward compatibility for the new field. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Benchmark Comparison ReportBaseline: Current: Summary
All benchmark results (sec/op)
Threshold: >10% with p < 0.05 | Generated: 2026-05-26 10:40 UTC |
Tightens policyFromSettings to reject: - NaN or +/-Inf MinMiningTxFee (the previous < 0 / > MaxUint64 check is always false for NaN, so a NaN fee silently cast to a wrong uint64). - Negative MaxScriptSizePolicy / MaxTxSizePolicy / MaxTxSigopsCountsPolicy (signed-to-uint64 cast would wrap to a huge advertised value). Also harmonises the legacy min_mining_tx_fee field with fee_policy in getNodeStatusMessage: when policyFromSettings rejects the policy, both fields are omitted, so consumers can never see an invalid scalar fee alongside an absent fee_policy. Addresses review feedback on PR bsv-blockchain#943.
The status publisher fires every ~10s, so the previous Warnf for an invalid policy config would flood the logs on a misconfigured node. Gate the warning behind sync.Once so it's emitted at most once per process, and demote subsequent occurrences to Debugf. Also broaden the message to cover the out-of-uint64-range MinMiningTxFee branch in policyFromSettings. Addresses follow-up review feedback on PR bsv-blockchain#943.
|
|
Now that this is merged, a few non-blocking cleanup notes from the review for a follow-up PR if anyone picks this up: 1. Consistency comment is partially true ( The new code says "Keep legacy and new fields consistent: only advertise the scalar fee when the full policy is valid." That holds inside the 2. Missing
3. Type name is narrow (
4. Document the "0 = unlimited" convention For 5. Document the mixed JSON-key convention
6. Conversion-logic duplication
|



Summary
FeePolicyfield toNodeStatusMessageso peers can read each other's mining-fee rate, max script/tx size, and max sigops/tx directly from the P2Pnode_statustopic — no out-of-band/v1/policyHTTP call needed./v1/policyresponse, minusStandardFormatSupported(an ARC transport flag, not a Teranode policy).omitempty; legacymin_mining_tx_feeis unchanged. Older peers and dashboards continue to parse status messages unchanged.Why
Issue #4639 asks for the broader fee policy to be advertised over P2P. Today only a single
MinMiningTxFeescalar is exposed, which is not enough for peers to make informed relay decisions (e.g., skip a node that won't accept a tx's size or sigop count).Implementation notes
FeePolicyis defined inservices/p2p/message_types.go(package-local — no shared package introduced; the asset HTTPPolicytype is independent and untouched).policyFromSettings()performs the BSV/kB → sat/kB conversion with the same float bounds check used byservices/asset/httpimpl/get_policy.go, returningnilon out-of-range / negative configs rather than abortingnode_statuspublication.getNodeStatusMessage, published byhandleNodeStatusNotification, and relayed to local WebSocket subscribers byhandleNodeStatusTopic.Test plan
go build ./...— cleango vet ./services/p2p/...— cleango test -race ./services/p2p/... ./services/asset/httpimpl/...— passgolangci-lint run ./services/p2p/...— no new findings (pre-existingpreallocwarning inmock.gountouched)staticcheck ./services/p2p/...— cleanTestGetNodeStatusMessagesubtests cover both Policy-set and Policy-nil branchesservices/p2p/fee_policy_test.goverify wire format,omitemptybehaviour, and parsing of legacy payloads withoutfee_policyCloses #4639