Skip to content

Commit 0b1d192

Browse files
committed
PR feedback: utility function
Signed-off-by: Dave Crighton <dave.crighton@kaleido.io>
1 parent fe2e153 commit 0b1d192

3 files changed

Lines changed: 100 additions & 13 deletions

File tree

internal/operations/operation_updater.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,8 @@ package operations
1818

1919
import (
2020
"context"
21-
"encoding/hex"
2221
"fmt"
23-
"strings"
2422
"time"
25-
"unicode/utf8"
2623

2724
"github.com/hyperledger/firefly-common/pkg/config"
2825
"github.com/hyperledger/firefly-common/pkg/ffapi"
@@ -35,6 +32,7 @@ import (
3532
"github.com/hyperledger/firefly/internal/txcommon"
3633
"github.com/hyperledger/firefly/pkg/core"
3734
"github.com/hyperledger/firefly/pkg/database"
35+
"github.com/hyperledger/firefly/pkg/utils"
3836
)
3937

4038
type operationUpdaterBatch struct {
@@ -424,16 +422,8 @@ func (ou *operationUpdater) resolveOperation(ctx context.Context, ns string, id
424422
if status != "" {
425423
update = update.Set("status", status)
426424
}
427-
if errorMsg != nil {
428-
// PostgreSQL text columns reject null bytes and invalid UTF-8 sequences.
429-
// Null bytes (0x00) are valid UTF-8 but rejected by PostgreSQL, so check both.
430-
if !utf8.ValidString(*errorMsg) || strings.ContainsRune(*errorMsg, 0) {
431-
hexString := hex.EncodeToString([]byte(*errorMsg))
432-
log.L(ctx).Warnf("Error message contains invalid UTF-8 or null bytes - encoding as hex: %s", hexString)
433-
update = update.Set("error", hexString)
434-
} else {
435-
update = update.Set("error", *errorMsg)
436-
}
425+
if safeErr, ok := utils.DBSafeUTF8StringFromPtr(ctx, errorMsg); ok {
426+
update = update.Set("error", safeErr)
437427
}
438428
if output != nil {
439429
update = update.Set("output", output)

pkg/utils/dbstring.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright © 2026 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package utils
18+
19+
import (
20+
"context"
21+
"encoding/hex"
22+
"strings"
23+
"unicode/utf8"
24+
25+
"github.com/hyperledger/firefly-common/pkg/log"
26+
)
27+
28+
// DBSafeUTF8StringFromPtr returns a DB-safe UTF-8 string from a pointer, and true if the pointer
29+
// was non-nil. Strings containing invalid UTF-8 sequences or null bytes (which PostgreSQL rejects
30+
// in text columns even though null bytes are technically valid UTF-8) are hex-encoded instead,
31+
// with a warning logged.
32+
func DBSafeUTF8StringFromPtr(ctx context.Context, s *string) (string, bool) {
33+
if s == nil {
34+
return "", false
35+
}
36+
if !utf8.ValidString(*s) || strings.ContainsRune(*s, 0) {
37+
hexString := hex.EncodeToString([]byte(*s))
38+
log.L(ctx).Warnf("String contains invalid UTF-8 or null bytes - encoding as hex: %s", hexString)
39+
return hexString, true
40+
}
41+
return *s, true
42+
}

pkg/utils/dbstring_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright © 2026 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package utils
18+
19+
import (
20+
"context"
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
func TestDBSafeUTF8StringFromPtrNil(t *testing.T) {
27+
s, ok := DBSafeUTF8StringFromPtr(context.Background(), nil)
28+
assert.False(t, ok)
29+
assert.Equal(t, "", s)
30+
}
31+
32+
func TestDBSafeUTF8StringFromPtrValid(t *testing.T) {
33+
msg := "FF23021: EVM reverted: some normal error message"
34+
s, ok := DBSafeUTF8StringFromPtr(context.Background(), &msg)
35+
assert.True(t, ok)
36+
assert.Equal(t, msg, s)
37+
}
38+
39+
func TestDBSafeUTF8StringFromPtrInvalidUTF8(t *testing.T) {
40+
// Simulate the actual revert scenario: readable text with embedded ABI-encoded Error(string)
41+
// selector bytes (0x08, 0xc3, 0x79, 0xa0) and null byte padding, which is invalid UTF-8
42+
msg := "[OCPE]404/98 - \x08\xc3\x79\xa0\x00\x00\x00[TMM]404/16e"
43+
s, ok := DBSafeUTF8StringFromPtr(context.Background(), &msg)
44+
assert.True(t, ok)
45+
assert.Equal(t, "5b4f4350455d3430342f3938202d2008c379a00000005b544d4d5d3430342f313665", s)
46+
}
47+
48+
func TestDBSafeUTF8StringFromPtrNullBytesInValidUTF8(t *testing.T) {
49+
// Pure null bytes embedded in otherwise valid UTF-8 text.
50+
// utf8.ValidString returns true for this, but PostgreSQL rejects 0x00 in text columns.
51+
msg := "hello\x00world"
52+
s, ok := DBSafeUTF8StringFromPtr(context.Background(), &msg)
53+
assert.True(t, ok)
54+
assert.Equal(t, "68656c6c6f00776f726c64", s)
55+
}

0 commit comments

Comments
 (0)