Defensive audit finding.
POST /gov/vote currently fails open when the masternode snapshot is stale or empty. In routes/gov.js, stale snapshots produce an empty knownOutpoints set, and entries are rejected only when that set has entries and does not contain the submitted outpoint:
routes/gov.js:23-39 computes fresh from masternodesUpdatedAt.
routes/gov.js:200-207 sets knownOutpoints to new Set() when the snapshot is stale.
routes/gov.js:211-219 only rejects unknown outpoints when knownOutpoints.size > 0.
Production wiring in server.js:343-346 passes { masternodes: dataStore.masternodesArr, updatedAt: dataStore.masternodesUpdatedAt }. On cold boot masternodesUpdatedAt starts at 0, and services/masternodeTracker.js:37-77 only stamps it after a successful interval refresh. If the tracker has not completed its first refresh, or RPC/list refresh is down long enough to exceed the 30s freshness window, /gov/vote bypasses the current-masternode membership guard and relays any structurally valid, signed entry to Core. The existing test tests/gov.routes.test.js:373-389 explicitly locks in this fail-open behavior.
Impact:
This does not expose WIFs and still requires Core-valid vote signatures. However, during tracker outages an authenticated account can use the backend as a generic voteraw relay instead of being constrained to the backend's current live masternode view. That weakens defense-in-depth for last-minute voting workflows, compromised/pre-collected signatures, and RPC resource protection.
Suggested hardening:
- Fail closed when the masternode snapshot is stale, e.g. return
503 { error: "masternode_cache_stale" } before relay.
- Treat a fresh-but-empty
knownOutpoints set as suspicious on mainnet and fail closed unless explicitly configured otherwise for tests/dev.
- Consider kicking an immediate tracker refresh on boot instead of waiting for the first interval tick.
- Update the test currently named
fails open to voteraw when the masternode cache snapshot is stale to assert the safer behavior.
Defensive audit finding.
POST /gov/votecurrently fails open when the masternode snapshot is stale or empty. Inroutes/gov.js, stale snapshots produce an emptyknownOutpointsset, and entries are rejected only when that set has entries and does not contain the submitted outpoint:routes/gov.js:23-39computesfreshfrommasternodesUpdatedAt.routes/gov.js:200-207setsknownOutpointstonew Set()when the snapshot is stale.routes/gov.js:211-219only rejects unknown outpoints whenknownOutpoints.size > 0.Production wiring in
server.js:343-346passes{ masternodes: dataStore.masternodesArr, updatedAt: dataStore.masternodesUpdatedAt }. On cold bootmasternodesUpdatedAtstarts at0, andservices/masternodeTracker.js:37-77only stamps it after a successful interval refresh. If the tracker has not completed its first refresh, or RPC/list refresh is down long enough to exceed the 30s freshness window,/gov/votebypasses the current-masternode membership guard and relays any structurally valid, signed entry to Core. The existing testtests/gov.routes.test.js:373-389explicitly locks in this fail-open behavior.Impact:
This does not expose WIFs and still requires Core-valid vote signatures. However, during tracker outages an authenticated account can use the backend as a generic
voterawrelay instead of being constrained to the backend's current live masternode view. That weakens defense-in-depth for last-minute voting workflows, compromised/pre-collected signatures, and RPC resource protection.Suggested hardening:
503 { error: "masternode_cache_stale" }before relay.knownOutpointsset as suspicious on mainnet and fail closed unless explicitly configured otherwise for tests/dev.fails open to voteraw when the masternode cache snapshot is staleto assert the safer behavior.