Skip to content
Merged
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 faucet-app/chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ apiVersion: v2
description: Faucet App Helm chart
name: faucet-app
version: 1.0.0
appVersion: "1.4.0"
appVersion: "1.4.1"
2 changes: 1 addition & 1 deletion nitronode/chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ apiVersion: v2
description: Nitronode Helm chart
name: nitronode
version: 1.0.0
appVersion: "1.4.0"
appVersion: "1.4.1"
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- +goose Up

-- block_hash was added as CHAR(66) NOT NULL DEFAULT '', so rows predating the
-- column (and any default insert) hold a space-padded empty string rather than a
-- real hash. Drop the NOT NULL constraint and the empty default, then normalize
-- those padded empties to NULL so the reconciler's empty-hash guard recognizes
-- "no recorded hash" instead of treating it as a reorged-out block.
ALTER TABLE contract_events ALTER COLUMN block_hash DROP DEFAULT;
ALTER TABLE contract_events ALTER COLUMN block_hash DROP NOT NULL;
UPDATE contract_events SET block_hash = NULL WHERE TRIM(block_hash) = '';

-- +goose Down

UPDATE contract_events SET block_hash = '' WHERE block_hash IS NULL;
ALTER TABLE contract_events ALTER COLUMN block_hash SET DEFAULT '';
ALTER TABLE contract_events ALTER COLUMN block_hash SET NOT NULL;
4 changes: 2 additions & 2 deletions nitronode/store/database/contract_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (s *DBStore) GetLatestContractEventBlockHashAndNumber(contractAddress strin
if err != nil {
return 0, "", err
}
return ev.BlockNumber, ev.BlockHash, nil
return ev.BlockNumber, strings.TrimSpace(ev.BlockHash), nil
}

// GetPreviousDistinctBlockHash returns the block_number and block_hash of the highest
Expand All @@ -104,5 +104,5 @@ func (s *DBStore) GetPreviousDistinctBlockHash(contractAddress string, blockchai
if err != nil {
return 0, "", err
}
return ev.BlockNumber, ev.BlockHash, nil
return ev.BlockNumber, strings.TrimSpace(ev.BlockHash), nil
}
52 changes: 52 additions & 0 deletions nitronode/store/database/contract_event_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package database

import (
"strings"
"testing"

"github.com/layer-3/nitrolite/pkg/core"
Expand Down Expand Up @@ -82,6 +83,57 @@ func TestGetLatestContractEventBlockNumber(t *testing.T) {
})
}

// TestGetContractEventBlockHash_TrimsPaddedEmpty is the regression guard for the
// reorg-reconciliation bug: block_hash was a CHAR(66) NOT NULL DEFAULT '' column,
// so legacy/pre-migration rows read back as a 66-space-padded string rather than "".
// The reconciler's empty-hash guard compares against "", so an untrimmed padded
// value slipped through, got fed to common.HexToHash (-> zero hash), and made every
// stored block look reorged on every chain. The getters now TrimSpace so a padded
// empty collapses to "" the guard recognizes, while real 0x… hashes pass through.
func TestGetContractEventBlockHash_TrimsPaddedEmpty(t *testing.T) {
db, cleanup := SetupTestDB(t)
defer cleanup()

store := NewDBStore(db)

contractAddress := "0x1234567890123456789012345678901234567890"
blockchainID := uint64(1)
paddedEmpty := strings.Repeat(" ", 66) // mimics CHAR(66) padding of ''
realHash := "0x" + strings.Repeat("ab", 32)

t.Run("latest padded-empty hash trims to empty string", func(t *testing.T) {
require.NoError(t, store.StoreContractEvent(core.BlockchainEvent{
ContractAddress: contractAddress, BlockchainID: blockchainID, Name: "E1",
BlockNumber: 100, BlockHash: paddedEmpty, TransactionHash: "0xaaa", LogIndex: 0,
}))

num, hash, err := store.GetLatestContractEventBlockHashAndNumber(contractAddress, blockchainID)
require.NoError(t, err)
assert.Equal(t, uint64(100), num)
assert.Equal(t, "", hash, "padded-empty block_hash must trim to \"\" so the reconciler guard fires")
})

t.Run("previous padded-empty hash trims to empty string", func(t *testing.T) {
// A newer row sits above the padded-empty one so the "below" query returns the legacy row.
require.NoError(t, store.StoreContractEvent(core.BlockchainEvent{
ContractAddress: contractAddress, BlockchainID: blockchainID, Name: "E2",
BlockNumber: 200, BlockHash: realHash, TransactionHash: "0xbbb", LogIndex: 0,
}))

num, hash, err := store.GetPreviousDistinctBlockHash(contractAddress, blockchainID, 200)
require.NoError(t, err)
assert.Equal(t, uint64(100), num)
assert.Equal(t, "", hash, "padded-empty block_hash must trim to \"\" mid-walk")
})

t.Run("real hash is preserved (not over-trimmed)", func(t *testing.T) {
num, hash, err := store.GetLatestContractEventBlockHashAndNumber(contractAddress, blockchainID)
require.NoError(t, err)
assert.Equal(t, uint64(200), num)
assert.Equal(t, realHash, hash)
})
}

func TestIsContractEventProcessed(t *testing.T) {
db, cleanup := SetupTestDB(t)
defer cleanup()
Expand Down
2 changes: 1 addition & 1 deletion playground/chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ apiVersion: v2
description: Nitrolite Playground Helm chart
name: playground
version: 1.0.0
appVersion: "1.4.0"
appVersion: "1.4.1"
Binary file added sdk/mcp/mcp-publisher
Binary file not shown.
Binary file added sdk/mcp/mcp-publisher.tar.gz
Binary file not shown.
4 changes: 2 additions & 2 deletions sdk/mcp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/mcp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yellow-org/sdk-mcp",
"version": "1.4.0",
"version": "1.4.1",
"description": "Unified MCP server for Yellow SDK and Nitrolite protocol context for AI agents and IDEs",
"type": "module",
"mcpName": "io.github.layer-3/yellow-sdk-mcp",
Expand Down
4 changes: 2 additions & 2 deletions sdk/mcp/server.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.layer-3/yellow-sdk-mcp",
"description": "MCP server exposing Yellow SDK and Nitrolite protocol reference material to AI agents and IDEs.",
"version": "1.4.0",
"version": "1.4.1",
"repository": {
"url": "https://github.com/layer-3/nitrolite",
"source": "github"
Expand All @@ -11,7 +11,7 @@
{
"registryType": "npm",
"identifier": "@yellow-org/sdk-mcp",
"version": "1.4.0",
"version": "1.4.1",
"transport": {
"type": "stdio"
}
Expand Down
6 changes: 3 additions & 3 deletions sdk/ts-compat/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/ts-compat/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yellow-org/sdk-compat",
"version": "1.4.0",
"version": "1.4.1",
"description": "Curated migration layer preserving selected Nitrolite SDK v0.5.3 app-facing APIs over the v1 runtime.",
"type": "module",
"sideEffects": false,
Expand Down
4 changes: 2 additions & 2 deletions sdk/ts/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/ts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yellow-org/sdk",
"version": "1.4.0",
"version": "1.4.1",
"description": "The Yellow SDK empowers developers to build high-performance, scalable web3 applications using state channels. It's designed to provide near-instant transactions and significantly improved user experiences by minimizing direct blockchain interactions.",
"type": "module",
"main": "dist/index.js",
Expand Down
Loading