Skip to content

Commit 1791f53

Browse files
committed
address review comments
Signed-off-by: Chengxuan Xing <[email protected]>
1 parent f4722e2 commit 1791f53

File tree

6 files changed

+104
-112
lines changed

6 files changed

+104
-112
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/hashicorp/golang-lru v1.0.2
88
github.com/hyperledger/firefly-common v1.5.6-0.20250630201730-e234335c0381
99
github.com/hyperledger/firefly-signer v1.1.21
10-
github.com/hyperledger/firefly-transaction-manager v1.4.1-0.20251003113149-46c9271ca66e
10+
github.com/hyperledger/firefly-transaction-manager v1.4.1-0.20251017153149-d6f944cab6b6
1111
github.com/sirupsen/logrus v1.9.3
1212
github.com/spf13/cobra v1.8.0
1313
github.com/stretchr/testify v1.9.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ github.com/hyperledger/firefly-signer v1.1.21 h1:r7cTOw6e/6AtiXLf84wZy6Z7zppzlc1
106106
github.com/hyperledger/firefly-signer v1.1.21/go.mod h1:axrlSQeKrd124UdHF5L3MkTjb5DeTcbJxJNCZ3JmcWM=
107107
github.com/hyperledger/firefly-transaction-manager v1.4.1-0.20251003113149-46c9271ca66e h1:jUumirQuZR8cQCBq9BNmpLM4rKo9ahQPdFzGvf/RYIs=
108108
github.com/hyperledger/firefly-transaction-manager v1.4.1-0.20251003113149-46c9271ca66e/go.mod h1:KHGvK/QqD2jpRZ04lQoX/k1T2o644NCkRlr3FbvKqnA=
109+
github.com/hyperledger/firefly-transaction-manager v1.4.1-0.20251017153149-d6f944cab6b6 h1:FGTXGnOoAaFkdA3n3SMYDXmHePrpX425P5YyljnaSyU=
110+
github.com/hyperledger/firefly-transaction-manager v1.4.1-0.20251017153149-d6f944cab6b6/go.mod h1:KHGvK/QqD2jpRZ04lQoX/k1T2o644NCkRlr3FbvKqnA=
109111
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
110112
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
111113
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=

internal/ethereum/confirmation_reconciler.go

Lines changed: 59 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// limitations under the License.
1616

1717
// The confirmation reconciler manages transaction confirmation queues by:
18-
// - Copying blocks from the canonical chain and the existing confirmation queue
18+
// - Copying blocks from the in-memory partial chain and the existing confirmation queue
1919
// - Detecting blockchain forks and rebuilding confirmation queues when necessary
2020
// - Filling gaps in confirmation queues by fetching missing blocks
2121
// - Determining when transactions have reached the target confirmation count
@@ -32,9 +32,9 @@ import (
3232
)
3333

3434
// reconcileConfirmationsForTransaction reconciles the confirmation queue for a transaction
35-
func (bl *blockListener) reconcileConfirmationsForTransaction(ctx context.Context, txHash string, existingConfirmations []*ffcapi.MinimalBlockInfo, targetConfirmationCount uint64) (*ffcapi.ConfirmationMapUpdateResult, error) {
35+
func (bl *blockListener) reconcileConfirmationsForTransaction(ctx context.Context, txHash string, existingConfirmations []*ffcapi.MinimalBlockInfo, targetConfirmationCount uint64) (*ffcapi.ConfirmationUpdateResult, error) {
3636
// Initialize the result with existing confirmations
37-
reconcileResult := &ffcapi.ConfirmationMapUpdateResult{
37+
reconcileResult := &ffcapi.ConfirmationUpdateResult{
3838
Confirmations: existingConfirmations,
3939
NewFork: false,
4040
Confirmed: false,
@@ -50,31 +50,52 @@ func (bl *blockListener) reconcileConfirmationsForTransaction(ctx context.Contex
5050

5151
if txBlockInfo == nil {
5252
log.L(ctx).Debugf("Transaction %s not found in any block", txHash)
53-
return reconcileResult, nil
53+
return nil, i18n.NewError(ctx, msgs.MsgTransactionNotFound, txHash)
5454
}
5555

5656
return bl.compareAndUpdateConfirmationQueue(ctx, reconcileResult, txBlockInfo, targetConfirmationCount)
5757
}
5858

5959
// compareAndUpdateConfirmationQueue orchestrates the confirmation reconciliation process.
60-
// It builds new confirmations from the canonical chain and fills gaps in the confirmation queue.
61-
func (bl *blockListener) compareAndUpdateConfirmationQueue(ctx context.Context, reconcileResult *ffcapi.ConfirmationMapUpdateResult, txBlockInfo *ffcapi.MinimalBlockInfo, targetConfirmationCount uint64) (*ffcapi.ConfirmationMapUpdateResult, error) {
60+
// It builds new confirmations from the in-memory partial chain and fills gaps in the confirmation queue.
61+
func (bl *blockListener) compareAndUpdateConfirmationQueue(ctx context.Context, reconcileResult *ffcapi.ConfirmationUpdateResult, txBlockInfo *ffcapi.MinimalBlockInfo, targetConfirmationCount uint64) (*ffcapi.ConfirmationUpdateResult, error) {
6262
var err error
63-
// Build new confirmations from the canonical chain and get existing confirmations
64-
newConfirmationsWithoutTxBlock, lastValidatedBlock, returnResult := bl.buildConfirmationQueueUsingCanonicalChain(ctx, reconcileResult, txBlockInfo, targetConfirmationCount)
65-
if returnResult {
66-
return reconcileResult, nil
63+
// Build new confirmations from the in-memory partial chain and get existing confirmations
64+
newConfirmationsWithoutTxBlock, lastValidatedBlock, err := bl.buildConfirmationQueueUsingInMemoryPartialChain(ctx, txBlockInfo, targetConfirmationCount)
65+
if err != nil {
66+
return nil, err
6767
}
6868

6969
// Initialize confirmation map and get existing confirmations
70-
// the init must happen after the canonical chain check to avoid
71-
// confirming blocks that are not yet validated in the canonical chain
72-
existingConfirmations := bl.initializeConfirmationMap(reconcileResult, txBlockInfo)
70+
// the init must happen after the in-memory partial chain check to avoid
71+
// confirming blocks that are not yet validated in the in-memory partial chain
72+
var existingConfirmations []*ffcapi.MinimalBlockInfo
73+
// If no existing confirmations, initialize with the transaction block
74+
if len(reconcileResult.Confirmations) == 0 {
75+
reconcileResult.Confirmations = []*ffcapi.MinimalBlockInfo{txBlockInfo}
76+
existingConfirmations = nil
77+
} else {
78+
// Validate existing confirmations against the current transaction block
79+
existingQueue := reconcileResult.Confirmations
80+
if len(existingQueue) > 0 {
81+
existingTxBlock := existingQueue[0]
82+
if !existingTxBlock.Equal(txBlockInfo) {
83+
// Transaction block mismatch indicates a fork - rebuild confirmation queue
84+
reconcileResult.NewFork = true
85+
reconcileResult.Confirmations = []*ffcapi.MinimalBlockInfo{txBlockInfo}
86+
existingConfirmations = nil
87+
} else {
88+
existingConfirmations = existingQueue
89+
}
90+
} else {
91+
existingConfirmations = existingQueue
92+
}
93+
}
7394

7495
// Special case: if targetConfirmationCount is 0, transaction is immediately confirmed
7596
if targetConfirmationCount == 0 {
76-
reconcileResult.Confirmed = true
7797
reconcileResult.Confirmations = []*ffcapi.MinimalBlockInfo{txBlockInfo}
98+
reconcileResult.Confirmed = true
7899
return reconcileResult, nil
79100
}
80101

@@ -83,88 +104,63 @@ func (bl *blockListener) compareAndUpdateConfirmationQueue(ctx context.Context,
83104
var newFork bool
84105
confirmations, newFork, err = bl.checkAndFillInGap(ctx, newConfirmationsWithoutTxBlock, existingConfirmations, txBlockInfo, targetConfirmationCount, lastValidatedBlock)
85106
if err != nil {
86-
return reconcileResult, err
107+
return nil, err
87108
}
88109
reconcileResult.NewFork = newFork
89110
reconcileResult.Confirmations = confirmations
111+
reconcileResult.Confirmed = uint64(len(confirmations)) >= targetConfirmationCount+1
90112
return reconcileResult, err
91113
}
92114

93-
// buildConfirmationQueueUsingCanonicalChain builds the confirmation queue using the in-memory canonical chain.
94-
// It does not modify the canonical chain itself, only reads from it.
95-
// This function holds a read lock on the canonical chain, so it should not make long-running queries.
96-
func (bl *blockListener) buildConfirmationQueueUsingCanonicalChain(ctx context.Context, reconcileResult *ffcapi.ConfirmationMapUpdateResult, txBlockInfo *ffcapi.MinimalBlockInfo, targetConfirmationCount uint64) (newConfirmationsWithoutTxBlock []*ffcapi.MinimalBlockInfo, lastValidatedBlock *ffcapi.MinimalBlockInfo, returnResult bool) {
115+
// buildConfirmationQueueUsingInMemoryPartialChain builds the confirmation queue using the in-memory partial chain.
116+
// It does not modify the in-memory partial chain itself, only reads from it.
117+
// This function holds a read lock on the in-memory partial chain, so it should not make long-running queries.
118+
func (bl *blockListener) buildConfirmationQueueUsingInMemoryPartialChain(ctx context.Context, txBlockInfo *ffcapi.MinimalBlockInfo, targetConfirmationCount uint64) (newConfirmationsWithoutTxBlock []*ffcapi.MinimalBlockInfo, lastValidatedBlock *ffcapi.MinimalBlockInfo, err error) {
97119
bl.mux.RLock()
98120
defer bl.mux.RUnlock()
99121
txBlockNumber := txBlockInfo.BlockNumber.Uint64()
100122
targetBlockNumber := txBlockInfo.BlockNumber.Uint64() + targetConfirmationCount
101123

102-
// Check if the canonical chain has caught up to the transaction block
124+
// Check if the in-memory partial chain has caught up to the transaction block
103125
chainTail := bl.canonicalChain.Back().Value.(*ffcapi.MinimalBlockInfo)
104126
if chainTail == nil || chainTail.BlockNumber.Uint64() < txBlockNumber {
105-
log.L(ctx).Debugf("Canonical chain is waiting for the transaction block %d to be indexed", txBlockNumber)
106-
return nil, nil, true
127+
log.L(ctx).Debugf("in-memory partial chain is waiting for the transaction block %d to be indexed", txBlockNumber)
128+
return nil, nil, i18n.NewError(ctx, msgs.MsgInMemoryPartialChainNotCaughtUp, txBlockNumber, txBlockInfo.BlockHash)
107129
}
108130

109131
// Build new confirmations from blocks after the transaction block
110132

111133
newConfirmationsWithoutTxBlock = []*ffcapi.MinimalBlockInfo{}
112-
currentBlock := bl.canonicalChain.Front()
113-
for currentBlock != nil {
114-
currentBlockInfo := currentBlock.Value.(*ffcapi.MinimalBlockInfo)
134+
nextInMemoryBlock := bl.canonicalChain.Front()
135+
for nextInMemoryBlock != nil {
136+
nextInMemoryBlockInfo := nextInMemoryBlock.Value.(*ffcapi.MinimalBlockInfo)
115137

116138
// If we've reached the target confirmation count, mark as confirmed
117-
if currentBlockInfo.BlockNumber.Uint64() > targetBlockNumber {
118-
reconcileResult.Confirmed = true
119-
// if the canonical chain contains the next block after the target block number,
139+
if nextInMemoryBlockInfo.BlockNumber.Uint64() > targetBlockNumber {
140+
// if the in-memory partial chain contains the next block after the target block number,
120141
// and the new confirmations queue is empty,
121142
// we set the last validated block to the next block, so the downstream function can use it validate blocks before it
122-
if len(newConfirmationsWithoutTxBlock) == 0 && currentBlockInfo.BlockNumber.Uint64() == targetBlockNumber+1 {
123-
lastValidatedBlock = currentBlockInfo
143+
if len(newConfirmationsWithoutTxBlock) == 0 && nextInMemoryBlockInfo.BlockNumber.Uint64() == targetBlockNumber+1 {
144+
lastValidatedBlock = nextInMemoryBlockInfo
124145
}
125146
break
126147
}
127148

128149
// Skip blocks at or before the transaction block
129-
if currentBlockInfo.BlockNumber.Uint64() <= txBlockNumber {
130-
currentBlock = currentBlock.Next()
150+
if nextInMemoryBlockInfo.BlockNumber.Uint64() <= txBlockNumber {
151+
nextInMemoryBlock = nextInMemoryBlock.Next()
131152
continue
132153
}
133154

134155
// Add blocks after the transaction block to confirmations
135156
newConfirmationsWithoutTxBlock = append(newConfirmationsWithoutTxBlock, &ffcapi.MinimalBlockInfo{
136-
BlockHash: currentBlockInfo.BlockHash,
137-
BlockNumber: fftypes.FFuint64(currentBlockInfo.BlockNumber.Uint64()),
138-
ParentHash: currentBlockInfo.ParentHash,
157+
BlockHash: nextInMemoryBlockInfo.BlockHash,
158+
BlockNumber: fftypes.FFuint64(nextInMemoryBlockInfo.BlockNumber.Uint64()),
159+
ParentHash: nextInMemoryBlockInfo.ParentHash,
139160
})
140-
currentBlock = currentBlock.Next()
161+
nextInMemoryBlock = nextInMemoryBlock.Next()
141162
}
142-
return newConfirmationsWithoutTxBlock, lastValidatedBlock, false
143-
}
144-
145-
// initializeConfirmationMap initializes the confirmation map with the transaction block
146-
// and validates existing confirmations against the current transaction block.
147-
// Returns existing confirmations if valid, or nil if a fork is detected.
148-
func (bl *blockListener) initializeConfirmationMap(reconcileResult *ffcapi.ConfirmationMapUpdateResult, txBlockInfo *ffcapi.MinimalBlockInfo) []*ffcapi.MinimalBlockInfo {
149-
// If no existing confirmations, initialize with the transaction block
150-
if len(reconcileResult.Confirmations) == 0 {
151-
reconcileResult.Confirmations = []*ffcapi.MinimalBlockInfo{txBlockInfo}
152-
return nil
153-
}
154-
155-
// Validate existing confirmations against the current transaction block
156-
existingQueue := reconcileResult.Confirmations
157-
if len(existingQueue) > 0 {
158-
existingTxBlock := existingQueue[0]
159-
if !existingTxBlock.Equal(txBlockInfo) {
160-
// Transaction block mismatch indicates a fork - rebuild confirmation queue
161-
reconcileResult.NewFork = true
162-
reconcileResult.Confirmations = []*ffcapi.MinimalBlockInfo{txBlockInfo}
163-
return nil
164-
}
165-
}
166-
167-
return existingQueue
163+
return newConfirmationsWithoutTxBlock, lastValidatedBlock, nil
168164
}
169165

170166
// checkAndFillInGap validates existing confirmations, detects forks, and fills gaps
@@ -254,6 +250,6 @@ func (bl *blockListener) checkAndFillInGap(ctx context.Context, newConfirmations
254250

255251
// ReconcileConfirmationsForTransaction is the public API for reconciling transaction confirmations.
256252
// It delegates to the blockListener's internal reconciliation logic.
257-
func (c *ethConnector) ReconcileConfirmationsForTransaction(ctx context.Context, txHash string, existingConfirmations []*ffcapi.MinimalBlockInfo, targetConfirmationCount uint64) (*ffcapi.ConfirmationMapUpdateResult, error) {
253+
func (c *ethConnector) ReconcileConfirmationsForTransaction(ctx context.Context, txHash string, existingConfirmations []*ffcapi.MinimalBlockInfo, targetConfirmationCount uint64) (*ffcapi.ConfirmationUpdateResult, error) {
258254
return c.blockListener.reconcileConfirmationsForTransaction(ctx, txHash, existingConfirmations, targetConfirmationCount)
259255
}

0 commit comments

Comments
 (0)