Skip to content

Add seed node#45

Open
crappyrules wants to merge 2 commits intoblocknetprivacy:masterfrom
crappyrules:add-seed-node
Open

Add seed node#45
crappyrules wants to merge 2 commits intoblocknetprivacy:masterfrom
crappyrules:add-seed-node

Conversation

@crappyrules
Copy link
Copy Markdown
Contributor

adding additional seed node to seed_resolve.go
adding additional lock-fixes to resolve node crashes.

… under the write lock

Root cause
----------
The blocknet-core binary could crash with

    fatal error: concurrent map read and map write

in goroutines reaching (*Chain).nextDifficultyLocked via TemplateParams.
TemplateParams held only c.mu.RLock while reading c.blocks and c.byHeight,
but several "*Locked" helpers transitively reachable from other RLock
holders were actually mutating chain state:

  * getBlockByHashLocked wrote to c.blocks and mutated the LRU cache on
    storage-fallback.
  * ensureCanonicalRingIndexLocked rebuilt c.canonicalRingIndex /
    c.canonicalRingHeights and set c.canonicalRingIndexTip /
    .Ready / .Dirty, and invoked getBlockByHashLocked internally.
  * isCanonicalRingMemberLocked (called from IsCanonicalRingMember and
    SelectRingMembersWithCommitments under RLock) invoked
    ensureCanonicalRingIndexLocked, so two concurrent RLock holders
    could write the same maps while another RLock reader
    (TemplateParams/nextDifficultyLocked) was iterating them.

Because sync.RWMutex.RLock permits multiple concurrent readers, any
mutation performed under RLock races with every other RLock-holding
reader. The Go runtime detects read+write on a map and aborts the
process.

Fix
---
Every mutation of shared chain maps (c.blocks, c.byHeight,
c.canonicalRingIndex, c.canonicalRingHeights, cache LRU/index) now
runs only under c.mu.Lock (the write lock). Read-only callers that
hold only c.mu.RLock never mutate state.

Changes in block.go:

1. loadFromStorage now eagerly builds the canonical ring-member index
   before returning. This happens while NewChain is still
   single-threaded, so no other goroutine can hold c.mu. After startup
   canonicalRingIndexReady=true and canonicalRingIndexDirty=false, and
   updateCanonicalRingIndexForConnect / updateCanonicalRingIndexForDisconnect
   keep it in sync incrementally from write-lock-only paths
   (addBlockInternal, reorganizeTo).

2. TruncateToHeight now calls ensureCanonicalRingIndexLocked after its
   in-memory mutations, while still holding the write lock, so readers
   never observe a dirty index.

3. isCanonicalRingMemberLocked is now a pure read. It no longer calls
   ensureCanonicalRingIndexLocked. A not-ready index returns false;
   callers that need guaranteed freshness must call
   ensureCanonicalRingIndexLocked themselves under the write lock
   (branchAwareRingMemberCheckerLocked already does this).

4. IsCanonicalRingMember gained an RLock fast path and a write-lock
   escalation slow path:
     - RLock + pre-built index lookup when the index is usable.
     - If the index is not ready, is dirty, or out of sync with the
       persisted tip, release RLock, acquire Lock, rebuild the index,
       then answer.

5. SelectRingMembersWithCommitments got the same RLock / write-lock
   escalation. The shuffle + ring assembly tail was extracted into
   the new pure-function helper finalizeRingSelection so the fast path
   can release c.mu before doing CPU-bound work.

6. Added helper canonicalRingMemberIndexUsableRLocked to decide
   fast vs slow path without mutating state.

7. Added lock-requirement doc comments to getBlockByHashLocked,
   ensureCanonicalRingIndexLocked, and isCanonicalRingMemberLocked
   so future callers can tell which lock they need to hold.

Verification
------------
  * `go build ./...` clean.
  * `go vet ./...` clean.
  * `go test -race -count=1 blocknet` passes (previously flagged the
    same maps under concurrent access).
  * Existing tests that reference chain.getBlockByHashLocked /
    chain.isCanonicalRingMemberLocked (block_branch_ringmember_test.go,
    chain_cache_bounds_test.go) continue to pass; they already call
    these helpers under chain.mu.Lock, which matches the documented
    requirement.
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