Skip to content

Commit 29edd48

Browse files
authored
FSC Update #1185 (#1452)
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
1 parent 61b0d87 commit 29edd48

28 files changed

Lines changed: 331 additions & 69 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/.idea/
22
.DS_Store
33
*.iml
4+
/.bob/
45

56
go.work
67
go.work.sum

ci/scripts/get-pr-failed-logs.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/bin/bash
2+
3+
# Exit on error, undefined variables, and pipe failures
4+
set -euo pipefail
5+
6+
# Check for required arguments
7+
if [ -z "${1:-}" ]; then
8+
echo "Usage: $0 <PR_NUMBER> [REPO]"
9+
echo "Example: $0 123"
10+
echo " $0 123 hyperledger/fabric"
11+
exit 1
12+
fi
13+
14+
PR_NUMBER=$1
15+
REPO_ARG=""
16+
17+
if [ -n "${2:-}" ]; then
18+
REPO_ARG="--repo $2"
19+
fi
20+
21+
# Ensure gh CLI is installed
22+
if ! command -v gh &> /dev/null; then
23+
echo "Error: GitHub CLI (gh) is not installed. Please install it first."
24+
exit 1
25+
fi
26+
27+
echo "🔍 Finding branch for PR #$PR_NUMBER..."
28+
BRANCH_NAME=$(gh pr view "$PR_NUMBER" $REPO_ARG --json headRefName --jq '.headRefName')
29+
30+
if [ -z "$BRANCH_NAME" ]; then
31+
echo "❌ Could not retrieve branch name for PR #$PR_NUMBER."
32+
exit 1
33+
fi
34+
35+
echo "✅ Found branch: $BRANCH_NAME"
36+
echo "🔍 Fetching the latest failed workflow run..."
37+
38+
# 1. Get ONLY the last (most recent) failed run ID for the branch
39+
LAST_RUN_ID=$(gh run list $REPO_ARG --branch "$BRANCH_NAME" --status failure -L 1 --json databaseId --jq '.[0].databaseId')
40+
41+
# jq returns "null" if the array is empty
42+
if [ -z "$LAST_RUN_ID" ] || [ "$LAST_RUN_ID" == "null" ]; then
43+
echo "✅ No failed runs found for PR #$PR_NUMBER (Branch: $BRANCH_NAME)."
44+
exit 0
45+
fi
46+
47+
# Create an output directory
48+
LOG_DIR="pr_${PR_NUMBER}_failed_logs"
49+
mkdir -p "$LOG_DIR"
50+
echo "📂 Logs will be saved in: ./${LOG_DIR}/"
51+
echo "Inspecting latest failed Run ID: $LAST_RUN_ID"
52+
53+
# 2. Get specific jobs within the run that have a "failure" conclusion
54+
FAILED_JOBS=$(gh run view "$LAST_RUN_ID" $REPO_ARG --json jobs --jq '.jobs[]? | select(.conclusion == "failure") | "\(.databaseId)|\(.name)"')
55+
56+
if [ -z "$FAILED_JOBS" ]; then
57+
echo " -> No specific failed jobs found in this run (could be a cancellation or workflow-level error)."
58+
exit 0
59+
fi
60+
61+
# 3. Process each failed job and fetch its specific log
62+
while IFS='|' read -r JOB_ID JOB_NAME; do
63+
if [ -z "$JOB_ID" ]; then
64+
continue
65+
fi
66+
67+
# Sanitize the job name to create a safe filename
68+
SAFE_JOB_NAME=$(echo "$JOB_NAME" | tr -s ' /:<>|' '_')
69+
LOG_FILE="${LOG_DIR}/${SAFE_JOB_NAME}_${JOB_ID}.log"
70+
71+
echo " ⬇️ Downloading and cleaning log for job: $JOB_NAME ($JOB_ID)"
72+
73+
# Use sed to strip everything up to and including the ISO8601 timestamp
74+
gh run view --job="$JOB_ID" $REPO_ARG --log | \
75+
sed -E 's/^.*[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+Z[[:space:]]*//' > "$LOG_FILE"
76+
77+
done <<< "$FAILED_JOBS"
78+
79+
echo "🚀 Done! Cleaned failed job logs have been extracted."

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/go-co-op/gocron/v2 v2.19.1
1313
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc
1414
github.com/hashicorp/go-uuid v1.0.3
15-
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260327032438-bc5ea61290df
15+
github.com/hyperledger-labs/fabric-smart-client v0.9.1-0.20260401145159-193d5af7c30b
1616
github.com/hyperledger/fabric-chaincode-go/v2 v2.3.0
1717
github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5
1818
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,8 +1063,8 @@ github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
10631063
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
10641064
github.com/hyperledger-labs/SmartBFT v0.0.0-20250503203013-eb005eef8866 h1:Mu/6NJsfl9g3wM15Ue7hqPq4LtgYDoABh8MO4u8aW4g=
10651065
github.com/hyperledger-labs/SmartBFT v0.0.0-20250503203013-eb005eef8866/go.mod h1:9aNHNXsCVy/leGz2gpTC1eOL5QecxbSAGjqsLh4T1LM=
1066-
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260327032438-bc5ea61290df h1:swfQCtsseF6OYSqeEfcLiN18jPXCEg6PU3cb9ZoQhWs=
1067-
github.com/hyperledger-labs/fabric-smart-client v0.8.3-0.20260327032438-bc5ea61290df/go.mod h1:kywkl6qr9jN3HtHxP9UCHkkGhjTRs+tmutsHgCcac14=
1066+
github.com/hyperledger-labs/fabric-smart-client v0.9.1-0.20260401145159-193d5af7c30b h1:3Q91ZtPpFoee5jbJCILDc51EAbAJM5a4Zsdd3qAqXME=
1067+
github.com/hyperledger-labs/fabric-smart-client v0.9.1-0.20260401145159-193d5af7c30b/go.mod h1:kywkl6qr9jN3HtHxP9UCHkkGhjTRs+tmutsHgCcac14=
10681068
github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc h1:3Ykk6MtyfnlzMOQry9zkxsoLWpCWZwDPqehO/BJwArM=
10691069
github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc/go.mod h1:Kofn6A6WWea1ZM8Rys5aBW9dszwJ7Ywa0kyyYL0TPYw=
10701070
github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 h1:B1Nt8hKb//KvgGRprk0h1t4lCnwhE9/ryb1WqfZbV+M=

integration/token/dvp/topology.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func Topology(opts Opts) []api.Topology {
4343
// FSC
4444
fscTopology := fsc.NewTopology()
4545
fscTopology.P2PCommunicationType = opts.CommType
46-
fscTopology.SetLogging(opts.FSCLogSpec, "")
46+
fscTopology.SetLogging(token2.RunnerDebug(opts.FSCLogSpec), "")
4747
fscTopology.EnableLogToFile()
4848
fscTopology.EnablePrometheusMetrics()
4949

integration/token/fungible/mixed/topology.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/hyperledger-labs/fabric-smart-client/integration/nwo/fsc/node"
1414
"github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token"
1515
fabric2 "github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token/fabric"
16+
token2 "github.com/hyperledger-labs/fabric-token-sdk/integration/token"
1617
"github.com/hyperledger-labs/fabric-token-sdk/integration/token/common"
1718
auditor3 "github.com/hyperledger-labs/fabric-token-sdk/integration/token/fungible/sdk/auditor"
1819
issuer3 "github.com/hyperledger-labs/fabric-token-sdk/integration/token/fungible/sdk/issuer"
@@ -47,7 +48,7 @@ func Topology(opts common.Opts) []api.Topology {
4748
// FSC
4849
fscTopology := fsc.NewTopology()
4950
fscTopology.P2PCommunicationType = opts.CommType
50-
fscTopology.SetLogging(opts.FSCLogSpec, "")
51+
fscTopology.SetLogging(token2.RunnerDebug(opts.FSCLogSpec), "")
5152

5253
issuer := fscTopology.NewTemplate("issuer")
5354
issuer1 := fscTopology.AddNodeFromTemplate("issuer1", issuer).

integration/token/fungible/topology/topology.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token"
2020
fabric2 "github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token/fabric"
2121
"github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token/generators/crypto/zkatdlognoghv1"
22+
token2 "github.com/hyperledger-labs/fabric-token-sdk/integration/token"
2223
"github.com/hyperledger-labs/fabric-token-sdk/integration/token/common"
2324
auditor2 "github.com/hyperledger-labs/fabric-token-sdk/integration/token/fungible/sdk/auditor"
2425
"github.com/hyperledger-labs/fabric-token-sdk/integration/token/fungible/sdk/endorser"
@@ -57,7 +58,7 @@ func Topology(opts common.Opts) []api.Topology {
5758
fscTopology.EnablePrometheusMetrics()
5859
fscTopology.EnableTracing(tracing.File)
5960
}
60-
fscTopology.SetLogging(opts.FSCLogSpec, "")
61+
fscTopology.SetLogging(token2.RunnerDebug(opts.FSCLogSpec), "")
6162

6263
issuer := fscTopology.AddNodeByName("issuer").AddOptions(
6364
fabric.WithOrganization("Org1"),
@@ -67,6 +68,9 @@ func Topology(opts common.Opts) []api.Topology {
6768
token.WithDefaultOwnerIdentity(),
6869
token.WithOwnerIdentity("issuer.owner"),
6970
)
71+
if opts.HSM {
72+
issuer.AddOptions(fabric.WithDefaultIdentityByHSM())
73+
}
7074
issuer.AddOptions(opts.ReplicationOpts.For("issuer")...)
7175

7276
newIssuer := fscTopology.AddNodeByName("newIssuer").AddOptions(

integration/token/interop/tests.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,8 @@ func TestFastExchange(network *integration.Infrastructure, sel *token2.ReplicaSe
320320

321321
CheckBalance(network, sel.Get("alice"), "", EUR, 20, token.WithTMSID(alpha))
322322

323-
gomega.Eventually(CheckBalanceReturnError).WithArguments(network, sel.Get("bob"), "", EUR, uint64(10), token.WithTMSID(alpha)).WithTimeout(1 * time.Minute).WithPolling(15 * time.Second).Should(gomega.Succeed())
323+
gomega.Eventually(CheckBalanceReturnError).WithArguments(network, sel.Get("bob"), "", EUR, uint64(10), token.WithTMSID(alpha)).WithTimeout(2 * time.Minute).WithPolling(15 * time.Second).Should(gomega.Succeed())
324324

325325
CheckBalance(network, sel.Get("alice"), "", USD, 10, token.WithTMSID(beta))
326-
gomega.Eventually(CheckBalanceReturnError).WithArguments(network, sel.Get("bob"), "", USD, uint64(20), token.WithTMSID(beta)).WithTimeout(1 * time.Minute).WithPolling(15 * time.Second).Should(gomega.Succeed())
326+
gomega.Eventually(CheckBalanceReturnError).WithArguments(network, sel.Get("bob"), "", USD, uint64(20), token.WithTMSID(beta)).WithTimeout(2 * time.Minute).WithPolling(15 * time.Second).Should(gomega.Succeed())
327327
}

integration/token/interop/topology.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/hyperledger-labs/fabric-smart-client/integration/nwo/fsc/node"
1414
"github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token"
1515
fabric2 "github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token/fabric"
16+
token2 "github.com/hyperledger-labs/fabric-token-sdk/integration/token"
1617

1718
"github.com/hyperledger-labs/fabric-token-sdk/integration/token/common"
1819
views3 "github.com/hyperledger-labs/fabric-token-sdk/integration/token/common/views"
@@ -30,7 +31,7 @@ func HTLCSingleFabricNetworkTopology(opts common.Opts) []api.Topology {
3031

3132
// FSC
3233
fscTopology := fsc.NewTopology()
33-
fscTopology.SetLogging(opts.FSCLogSpec, "")
34+
fscTopology.SetLogging(token2.RunnerDebug(opts.FSCLogSpec), "")
3435
fscTopology.P2PCommunicationType = opts.CommType
3536

3637
addIssuer(fscTopology).
@@ -76,7 +77,7 @@ func HTLCTwoFabricNetworksTopology(opts common.Opts) []api.Topology {
7677

7778
// FSC
7879
fscTopology := fsc.NewTopology()
79-
fscTopology.SetLogging(opts.FSCLogSpec, "")
80+
fscTopology.SetLogging(token2.RunnerDebug(opts.FSCLogSpec), "")
8081
fscTopology.P2PCommunicationType = opts.CommType
8182

8283
addIssuer(fscTopology).
@@ -141,7 +142,7 @@ func HTLCNoCrossClaimTopology(opts common.Opts) []api.Topology {
141142

142143
// FSC
143144
fscTopology := fsc.NewTopology()
144-
fscTopology.SetLogging(opts.FSCLogSpec, "")
145+
fscTopology.SetLogging(token2.RunnerDebug(opts.FSCLogSpec), "")
145146
fscTopology.P2PCommunicationType = opts.CommType
146147

147148
addIssuer(fscTopology).

integration/token/interop/views/htlc/claim.go

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0
77
package htlc
88

99
import (
10+
"bytes"
1011
"encoding/json"
1112
"time"
1213

@@ -40,7 +41,7 @@ type ClaimView struct {
4041
*Claim
4142
}
4243

43-
func (r *ClaimView) Call(context view.Context) (res interface{}, err error) {
44+
func (r *ClaimView) Call(ctx view.Context) (res interface{}, err error) {
4445
var tx *htlc.Transaction
4546
defer func() {
4647
if e := recover(); e != nil {
@@ -61,7 +62,7 @@ func (r *ClaimView) Call(context view.Context) (res interface{}, err error) {
6162
// Scan for the pre-image
6263
var err error
6364
preImage, err = htlc.ScanForPreImage(
64-
context,
65+
ctx,
6566
r.Script.HashInfo.Hash,
6667
r.Script.HashInfo.HashFunc,
6768
r.Script.HashInfo.HashEncoding,
@@ -71,41 +72,56 @@ func (r *ClaimView) Call(context view.Context) (res interface{}, err error) {
7172
assert.NoError(err, "failed to receive the preImage")
7273

7374
// double-check the value of the key
74-
tms, err := token.GetManagementService(context, token.WithTMSID(r.ScriptTMSID))
75+
tms, err := token.GetManagementService(ctx, token.WithTMSID(r.ScriptTMSID))
7576
assert.NoError(err, "failed getting management service")
76-
network := network.GetInstance(context, tms.Network(), tms.Channel())
77+
network := network.GetInstance(ctx, tms.Network(), tms.Channel())
7778
assert.NotNil(network, "failed getting network")
7879
ledger, err := network.Ledger()
7980
assert.NoError(err, "failed getting ledger")
8081
transferMetadataKey, err := ledger.TransferMetadataKey(htlc.ClaimKey(r.Script.HashInfo.Hash))
8182
assert.NoError(err, "failed getting transfer metadata key")
82-
stateValues, err := ledger.GetStates(context.Context(), tms.Namespace(), transferMetadataKey)
83-
assert.NoError(err, "failed getting states")
84-
assert.True(len(stateValues) == 1, "expected one state value")
85-
assert.Equal(preImage, stateValues[0], "pre-image mismatch [%s] vs [%s]", utils.Hashable(preImage), utils.Hashable(stateValues[0]))
83+
84+
// double-check the content of the ledger, retry a few time to give time to the committer
85+
runner := utils.NewRetryRunner(logger, 3, 1*time.Second, true)
86+
assert.NoError(err, runner.RunWithContext(ctx.Context(), func() error {
87+
logger.Debugf("check transfer metadata key [%s]...", transferMetadataKey)
88+
stateValues, err := ledger.GetStates(ctx.Context(), tms.Namespace(), transferMetadataKey)
89+
if err != nil {
90+
return err
91+
}
92+
logger.Debugf("check transfer metadata key [%s], got [%v]", transferMetadataKey, stateValues)
93+
if len(stateValues) != 1 {
94+
return errors.Errorf("expected 1 state, found %d", len(stateValues))
95+
}
96+
if !bytes.Equal(stateValues[0], r.PreImage) {
97+
return errors.Errorf("pre-image mismatch [%s] vs [%s]", utils.Hashable(preImage), utils.Hashable(stateValues[0]))
98+
}
99+
100+
return nil
101+
}))
86102
}
87103

88-
claimWallet := htlc.GetWallet(context, r.Wallet, token.WithTMSID(r.TMSID))
104+
claimWallet := htlc.GetWallet(ctx, r.Wallet, token.WithTMSID(r.TMSID))
89105
assert.NotNil(claimWallet, "wallet [%s] not found", r.Wallet)
90106

91-
matched, err := htlc.Wallet(context, claimWallet).ListByPreImage(context.Context(), preImage)
107+
matched, err := htlc.Wallet(ctx, claimWallet).ListByPreImage(ctx.Context(), preImage)
92108
assert.NoError(err, "htlc script has expired")
93109
assert.True(matched.Count() == 1, "expected only one htlc script to match [%s], got [%d]", view.Identity(preImage), matched.Count())
94110

95-
idProvider, err := id.GetProvider(context)
111+
idProvider, err := id.GetProvider(ctx)
96112
assert.NoError(err, "failed getting id provider")
97113
tx, err = htlc.NewAnonymousTransaction(
98-
context,
114+
ctx,
99115
ttx.WithAuditor(idProvider.Identity("auditor")),
100116
ttx.WithTMSID(r.TMSID),
101117
)
102118
assert.NoError(err, "failed to create an htlc transaction")
103119
assert.NoError(tx.Claim(claimWallet, matched.At(0), preImage), "failed adding a claim for [%s]", matched.At(0).Id)
104120

105-
_, err = context.RunView(htlc.NewCollectEndorsementsView(tx))
121+
_, err = ctx.RunView(htlc.NewCollectEndorsementsView(tx))
106122
assert.NoError(err, "failed to collect endorsements on htlc transaction")
107123

108-
_, err = context.RunView(htlc.NewOrderingAndFinalityView(tx))
124+
_, err = ctx.RunView(htlc.NewOrderingAndFinalityView(tx))
109125
assert.NoError(err, "failed to commit htlc transaction")
110126

111127
return tx.ID(), nil

0 commit comments

Comments
 (0)