Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/keeper/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/time v0.12.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Expand Down
1 change: 1 addition & 0 deletions cmd/keeper/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
Expand Down
4 changes: 3 additions & 1 deletion consensus/bor/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,9 @@ func (api *API) GetCurrentValidators() ([]*valset.Validator, error) {
return snap.ValidatorSet.Validators, nil
}

// GetRootHash returns the merkle root of the start-to-end blocks' headers
// GetRootHash returns the merkle root of the start-to-end blocks' headers.
// rootHashCache is normally initialized eagerly inside Bor.APIs (sync.Once);
// the lazy init below is kept as fallback for direct-API paths (e.g., tests) that don't go through Bor.APIs.
func (api *API) GetRootHash(start uint64, end uint64) (string, error) {
if err := api.initializeRootHashCache(); err != nil {
return "", err
Expand Down
24 changes: 23 additions & 1 deletion consensus/bor/bor.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ type Bor struct {
// ctx is cancelled when Close() is called, allowing in-flight operations to abort promptly.
ctx context.Context
ctxCancel context.CancelFunc

// api is the bor engine API instance reused across all callers (JSON-RPC and gRPC).
api *API
apiOnce sync.Once
}

type signer struct {
Expand Down Expand Up @@ -1520,11 +1524,29 @@ func (c *Bor) SealHash(header *types.Header) common.Hash {

// APIs implements consensus.Engine, returning the user facing RPC API to allow
// controlling the signer voting.
//
// The returned *API is cached on the first call so that per-API state (e.g.,
// rootHashCache) persists across calls. JSON-RPC only invokes APIs() once at
// node startup, but the gRPC backend fetches it on every handler call — without
// the cache those calls would each start from an empty state.
//
// rootHashCache is initialized here (inside the sync.Once) rather than lazily
// in GetRootHash so that concurrent gRPC handlers sharing the cached *API
// cannot race in initializeRootHashCache.
func (c *Bor) APIs(chain consensus.ChainHeaderReader) []rpc.API {
c.apiOnce.Do(func() {
a := &API{chain: chain, bor: c}
if err := a.initializeRootHashCache(); err != nil {
// log.Crit logs at the highest severity and then exits the process;
// This is currently unreachable (size is a constant in initializeRootHashCache),
log.Crit("bor: failed to initialize rootHashCache", "err", err)
}
c.api = a
})
return []rpc.API{{
Namespace: "bor",
Version: "1.0",
Service: &API{chain: chain, bor: c},
Service: c.api,
Public: false,
}}
}
Expand Down
14 changes: 14 additions & 0 deletions consensus/bor/bor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,20 @@ func TestAPIs_ReturnsBorNamespace(t *testing.T) {
require.Equal(t, "1.0", apis[0].Version)
}

// TestAPIs_ReturnsSameInstanceAcrossCalls verifies that repeated calls to APIs() must return the same *API
// so per-API state such as rootHashCache persists across calls. This matters for the gRPC backend
// which fetches APIs() on every handler invocation; returning a fresh *API each call defeats caching.
func TestAPIs_ReturnsSameInstanceAcrossCalls(t *testing.T) {
t.Parallel()
sp := &fakeSpanner{vals: []*valset.Validator{{Address: common.HexToAddress("0x1"), VotingPower: 1}}}
borCfg := defaultBorConfig()
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{}, uint64(time.Now().Unix()))

first := b.APIs(chain.HeaderChain())
second := b.APIs(chain.HeaderChain())
require.Same(t, first[0].Service, second[0].Service, "APIs must return the cached *API on repeated calls")
}

func TestClose_Idempotent(t *testing.T) {
t.Parallel()
sp := &fakeSpanner{vals: []*valset.Validator{{Address: common.HexToAddress("0x1"), VotingPower: 1}}}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ go 1.26.2
require (
github.com/0xPolygon/crand v1.0.3
github.com/0xPolygon/heimdall-v2 v0.6.0
github.com/0xPolygon/polyproto v0.0.7
github.com/0xPolygon/polyproto v0.0.8-0.20260423132317-7d955b45ef8a
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2
github.com/BurntSushi/toml v1.4.0
github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ github.com/0xPolygon/crand v1.0.3 h1:BYYflmgLhmGPEgqtopG4muq6wV6DOkwD8uPymNz5WeQ
github.com/0xPolygon/crand v1.0.3/go.mod h1:km4366oC7EVFl1xNUCwzxUXNM10swZqd8LZ0E5SgbAE=
github.com/0xPolygon/heimdall-v2 v0.6.0 h1:rA8RISMnns1w08PxTLvDBS5WiaTOFHJGSrhDWDJLtHc=
github.com/0xPolygon/heimdall-v2 v0.6.0/go.mod h1:fVkGiODG6cGLaDyrE3qxIrvz1rbUr4Zdrr3dOm2SPgg=
github.com/0xPolygon/polyproto v0.0.7 h1:Ody+kFyCRK4QXRPXbsP5pdxKrDgwAAXtFB8NPgaIxRs=
github.com/0xPolygon/polyproto v0.0.7/go.mod h1:2Iw93k2LismvckKKeXQITuhJH9vLbqOa212AMskH6no=
github.com/0xPolygon/polyproto v0.0.8-0.20260423132317-7d955b45ef8a h1:vVtSjO29FcFBZbNVsVGy6z3lv3RRr/sI1vPR7IzbZZE=
github.com/0xPolygon/polyproto v0.0.8-0.20260423132317-7d955b45ef8a/go.mod h1:2Iw93k2LismvckKKeXQITuhJH9vLbqOa212AMskH6no=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
Expand Down
Loading
Loading