From cd9755d2526c23a8ae1ad0e14b979f25d676a555 Mon Sep 17 00:00:00 2001 From: Chengxuan Xing Date: Fri, 26 Apr 2024 09:27:54 +0100 Subject: [PATCH] allow the action occurred time to be passed in Signed-off-by: Chengxuan Xing --- Makefile | 6 +-- .../leveldb/leveldb_persistence.go | 28 ++++++------ .../leveldb/leveldb_persistence_test.go | 44 +++++++++---------- .../persistence/postgres/transactions_test.go | 10 ++--- internal/persistence/postgres/txhistory.go | 12 ++--- .../persistence/postgres/txhistory_test.go | 24 +++++----- mocks/ffcapimocks/api.go | 2 +- .../transaction_handler_metrics.go | 2 +- mocks/persistencemocks/persistence.go | 12 ++--- mocks/persistencemocks/rich_query.go | 2 +- .../transaction_persistence.go | 2 +- .../managed_tx_event_handler.go | 2 +- mocks/txhandlermocks/transaction_handler.go | 2 +- mocks/wsmocks/web_socket_channels.go | 2 +- mocks/wsmocks/web_socket_server.go | 2 +- pkg/txhandler/simple/policyloop.go | 8 ++-- pkg/txhandler/simple/policyloop_test.go | 20 ++++----- .../simple/simple_transaction_handler.go | 18 ++++---- pkg/txhandler/txhandler.go | 4 +- 19 files changed, 100 insertions(+), 102 deletions(-) diff --git a/Makefile b/Makefile index 03ea775c..4d33299c 100644 --- a/Makefile +++ b/Makefile @@ -16,14 +16,12 @@ test: deps lint coverage.html: $(VGO) tool cover -html=coverage.txt coverage: test coverage.html -lint: ${LINT} +lint: + $(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2 GOGC=20 $(LINT) run -v --timeout 5m ${MOCKERY}: $(VGO) install github.com/vektra/mockery/v2@latest -${LINT}: - $(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2 - define makemock mocks: mocks-$(strip $(1))-$(strip $(2)) diff --git a/internal/persistence/leveldb/leveldb_persistence.go b/internal/persistence/leveldb/leveldb_persistence.go index 7f53a2c3..402b3cbe 100644 --- a/internal/persistence/leveldb/leveldb_persistence.go +++ b/internal/persistence/leveldb/leveldb_persistence.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -661,7 +661,7 @@ func (p *leveldbPersistence) DeleteTransaction(ctx context.Context, txID string) ) } -func (p *leveldbPersistence) setSubStatusInStruct(ctx context.Context, tx *apitypes.TXWithStatus, subStatus apitypes.TxSubStatus) { +func (p *leveldbPersistence) setSubStatusInStruct(ctx context.Context, tx *apitypes.TXWithStatus, subStatus apitypes.TxSubStatus, actionOccurred *fftypes.FFTime) { // See if the status being transitioned to is the same as the current status. // If so, there's nothing to do. @@ -674,7 +674,7 @@ func (p *leveldbPersistence) setSubStatusInStruct(ctx context.Context, tx *apity // If this is a change in status add a new record newStatus := &apitypes.TxHistoryStateTransitionEntry{ - Time: fftypes.Now(), + Time: actionOccurred, Status: subStatus, Actions: make([]*apitypes.TxHistoryActionEntry, 0), } @@ -692,16 +692,16 @@ func (p *leveldbPersistence) setSubStatusInStruct(ctx context.Context, tx *apity for _, statusType := range tx.DeprecatedHistorySummary { if statusType.Status == subStatus { // Just increment the counter and last timestamp - statusType.LastOccurrence = fftypes.Now() + statusType.LastOccurrence = actionOccurred statusType.Count++ return } } - tx.DeprecatedHistorySummary = append(tx.DeprecatedHistorySummary, &apitypes.TxHistorySummaryEntry{Status: subStatus, Count: 1, FirstOccurrence: fftypes.Now(), LastOccurrence: fftypes.Now()}) + tx.DeprecatedHistorySummary = append(tx.DeprecatedHistorySummary, &apitypes.TxHistorySummaryEntry{Status: subStatus, Count: 1, FirstOccurrence: actionOccurred, LastOccurrence: actionOccurred}) } -func (p *leveldbPersistence) AddSubStatusAction(ctx context.Context, txID string, subStatus apitypes.TxSubStatus, action apitypes.TxAction, info *fftypes.JSONAny, errInfo *fftypes.JSONAny) error { +func (p *leveldbPersistence) AddSubStatusAction(ctx context.Context, txID string, subStatus apitypes.TxSubStatus, action apitypes.TxAction, info *fftypes.JSONAny, errInfo *fftypes.JSONAny, actionOccurred *fftypes.FFTime) error { tx, err := p.getPersistedTX(ctx, txID) if err != nil { @@ -713,7 +713,7 @@ func (p *leveldbPersistence) AddSubStatusAction(ctx context.Context, txID string } // Ensure structure is updated to latest sub status - p.setSubStatusInStruct(ctx, tx, subStatus) + p.setSubStatusInStruct(ctx, tx, subStatus, actionOccurred) // See if this action exists in the list already since we only want to update the single entry, not // add a new one @@ -723,11 +723,11 @@ func (p *leveldbPersistence) AddSubStatusAction(ctx context.Context, txID string if entry.Action == action { alreadyRecordedAction = true entry.OccurrenceCount++ - entry.LastOccurrence = fftypes.Now() + entry.LastOccurrence = actionOccurred if errInfo != nil { entry.LastError = persistence.JSONOrString(errInfo) - entry.LastErrorTime = fftypes.Now() + entry.LastErrorTime = actionOccurred } if info != nil { @@ -740,15 +740,15 @@ func (p *leveldbPersistence) AddSubStatusAction(ctx context.Context, txID string if !alreadyRecordedAction { // If this is an entirely new action for this status entry, add it to the list newAction := &apitypes.TxHistoryActionEntry{ - Time: fftypes.Now(), + Time: actionOccurred, Action: action, - LastOccurrence: fftypes.Now(), + LastOccurrence: actionOccurred, OccurrenceCount: 1, } if errInfo != nil { newAction.LastError = persistence.JSONOrString(errInfo) - newAction.LastErrorTime = fftypes.Now() + newAction.LastErrorTime = actionOccurred } if info != nil { @@ -763,14 +763,14 @@ func (p *leveldbPersistence) AddSubStatusAction(ctx context.Context, txID string for _, actionType := range tx.DeprecatedHistorySummary { if actionType.Action == action { // Just increment the counter and last timestamp - actionType.LastOccurrence = fftypes.Now() + actionType.LastOccurrence = actionOccurred actionType.Count++ found = true break } } if !found { - tx.DeprecatedHistorySummary = append(tx.DeprecatedHistorySummary, &apitypes.TxHistorySummaryEntry{Action: action, Count: 1, FirstOccurrence: fftypes.Now(), LastOccurrence: fftypes.Now()}) + tx.DeprecatedHistorySummary = append(tx.DeprecatedHistorySummary, &apitypes.TxHistorySummaryEntry{Action: action, Count: 1, FirstOccurrence: actionOccurred, LastOccurrence: actionOccurred}) } return p.writeTransaction(ctx, tx, false) } diff --git a/internal/persistence/leveldb/leveldb_persistence_test.go b/internal/persistence/leveldb/leveldb_persistence_test.go index ba5f84e4..5f62ce96 100644 --- a/internal/persistence/leveldb/leveldb_persistence_test.go +++ b/internal/persistence/leveldb/leveldb_persistence_test.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -460,7 +460,7 @@ func TestAddSubStatusActionFail(t *testing.T) { tx := newTestTX("0x1234", apitypes.TxStatusPending) - err := p.AddSubStatusAction(ctx, tx.ID, apitypes.TxSubStatusTracking, apitypes.TxActionAssignNonce, nil, nil) + err := p.AddSubStatusAction(ctx, tx.ID, apitypes.TxSubStatusTracking, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.Error(t, err) } @@ -834,7 +834,7 @@ func TestManagedTXSubStatus(t *testing.T) { // Adding the same sub-status lots of times in succession should only result // in a single entry for that instance for i := 0; i < 100; i++ { - err := p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil) + err := p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.NoError(t, err) } @@ -845,7 +845,7 @@ func TestManagedTXSubStatus(t *testing.T) { // Adding a different type of sub-status should result in // a new entry in the list - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusTracking, apitypes.TxActionAssignNonce, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusTracking, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) @@ -855,9 +855,9 @@ func TestManagedTXSubStatus(t *testing.T) { // Even if many new types are added we shouldn't go over the // configured upper limit for i := 0; i < 100; i++ { - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusStale, apitypes.TxActionAssignNonce, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusStale, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.NoError(t, err) - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusTracking, apitypes.TxActionAssignNonce, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusTracking, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.NoError(t, err) } @@ -875,7 +875,7 @@ func TestManagedTXSubStatusRepeat(t *testing.T) { assert.NoError(t, err) // Add a sub-status - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.NoError(t, err) txh, err := p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -883,14 +883,14 @@ func TestManagedTXSubStatusRepeat(t *testing.T) { assert.Equal(t, 2, len(txh.DeprecatedHistorySummary)) // Add another sub-status - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusTracking, apitypes.TxActionSubmitTransaction, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusTracking, apitypes.TxActionSubmitTransaction, nil, nil, fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.Equal(t, 2, len(txh.History)) assert.Equal(t, 4, len(txh.DeprecatedHistorySummary)) // Add another that we've seen before - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, nil, fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.Equal(t, 3, len(txh.History)) // This goes up @@ -902,14 +902,14 @@ func TestManagedTXSubStatusAction(t *testing.T) { defer done() mtx := newTestTX("0x12345", apitypes.TxStatusPending) - err := p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil) + err := p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.Regexp(t, "FF21067", err) err = p.InsertTransactionWithNextNonce(ctx, mtx, func(ctx context.Context, signer string) (uint64, error) { return 12345, nil }) assert.NoError(t, err) // Add an action - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.NoError(t, err) txh, err := p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -917,7 +917,7 @@ func TestManagedTXSubStatusAction(t *testing.T) { assert.Nil(t, txh.History[0].Actions[0].LastErrorTime) // Add another action - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionRetrieveGasPrice, nil, fftypes.JSONAnyPtr(`{"gasError":"Acme Gas Oracle RC=12345"}`)) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionRetrieveGasPrice, nil, fftypes.JSONAnyPtr(`{"gasError":"Acme Gas Oracle RC=12345"}`), fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -925,7 +925,7 @@ func TestManagedTXSubStatusAction(t *testing.T) { assert.Equal(t, (*txh.History[0].Actions[1].LastError).String(), `{"gasError":"Acme Gas Oracle RC=12345"}`) // Add the same action which should cause the previous one to inc its counter - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionRetrieveGasPrice, fftypes.JSONAnyPtr(`{"info":"helloworld"}`), fftypes.JSONAnyPtr(`{"error":"nogood"}`)) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionRetrieveGasPrice, fftypes.JSONAnyPtr(`{"info":"helloworld"}`), fftypes.JSONAnyPtr(`{"error":"nogood"}`), fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -934,7 +934,7 @@ func TestManagedTXSubStatusAction(t *testing.T) { assert.Equal(t, 2, txh.History[0].Actions[1].OccurrenceCount) // Add the same action but with new error information should update the last error field - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionRetrieveGasPrice, nil, fftypes.JSONAnyPtr(`{"gasError":"Acme Gas Oracle RC=67890"}`)) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionRetrieveGasPrice, nil, fftypes.JSONAnyPtr(`{"gasError":"Acme Gas Oracle RC=67890"}`), fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -944,7 +944,7 @@ func TestManagedTXSubStatusAction(t *testing.T) { // Add a new type of action reason := "known_transaction" - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"reason":"`+reason+`"}`), nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"reason":"`+reason+`"}`), nil, fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -956,7 +956,7 @@ func TestManagedTXSubStatusAction(t *testing.T) { // Add one more type of action receiptId := "123456" - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionReceiveReceipt, fftypes.JSONAnyPtr(`{"receiptId":"`+receiptId+`"}`), nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionReceiveReceipt, fftypes.JSONAnyPtr(`{"receiptId":"`+receiptId+`"}`), nil, fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -972,13 +972,13 @@ func TestManagedTXSubStatusAction(t *testing.T) { // Add some new sub-status and actions to check max lengths are correct // Seen one of these before - should increase summary length by 1 - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusConfirmed, apitypes.TxActionReceiveReceipt, fftypes.JSONAnyPtr(`{"receiptId":"`+receiptId+`"}`), nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusConfirmed, apitypes.TxActionReceiveReceipt, fftypes.JSONAnyPtr(`{"receiptId":"`+receiptId+`"}`), nil, fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.Equal(t, 6, len(txh.DeprecatedHistorySummary)) // Seen both of these before - no change expected - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -1070,7 +1070,7 @@ func TestManagedTXSubStatusInvalidJSON(t *testing.T) { reason := "\"cannot-marshall\"" // Add a new type of action - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"reason":"`+reason+`"}`), nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"reason":"`+reason+`"}`), nil, fftypes.Now()) assert.NoError(t, err) txh, err := p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) assert.NoError(t, err) @@ -1095,7 +1095,7 @@ func TestManagedTXSubStatusMaxEntries(t *testing.T) { // first 50 for i := 0; i < 100; i++ { nextSubStatus = apitypes.TxSubStatus(fmt.Sprint(i)) - p.AddSubStatusAction(ctx, mtx.ID, nextSubStatus, apitypes.TxActionAssignNonce, nil, nil) + p.AddSubStatusAction(ctx, mtx.ID, nextSubStatus, apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) assert.NoError(t, err) } @@ -1114,7 +1114,7 @@ func TestMaxHistoryCountSetToZero(t *testing.T) { err := p.InsertTransactionWithNextNonce(ctx, mtx, func(ctx context.Context, signer string) (uint64, error) { return 12345, nil }) assert.NoError(t, err) - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, nil, fftypes.Now()) assert.NoError(t, err) txh, err := p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) @@ -1141,7 +1141,7 @@ func TestAddReceivedStatusWhenNothingSet(t *testing.T) { assert.NoError(t, err) assert.Nil(t, txh.History) - err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, nil) + err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, nil, fftypes.Now()) assert.NoError(t, err) txh, err = p.GetTransactionByIDWithStatus(ctx, mtx.ID, true) diff --git a/internal/persistence/postgres/transactions_test.go b/internal/persistence/postgres/transactions_test.go index e6c17a9f..424560e4 100644 --- a/internal/persistence/postgres/transactions_test.go +++ b/internal/persistence/postgres/transactions_test.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -103,13 +103,13 @@ func TestTransactionBasicValidationPSQL(t *testing.T) { assert.NoError(t, err) // A couple of transaction history entries - err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, fftypes.JSONAnyPtr(`{"nonce":"11111"}`), nil) + err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, fftypes.JSONAnyPtr(`{"nonce":"11111"}`), nil, fftypes.Now()) assert.NoError(t, err) - err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, fftypes.JSONAnyPtr(`"failed to submit 1"`)) + err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, fftypes.JSONAnyPtr(`"failed to submit 1"`), fftypes.Now()) assert.NoError(t, err) - err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, fftypes.JSONAnyPtr(`"failed to submit 2"`)) + err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, nil, fftypes.JSONAnyPtr(`"failed to submit 2"`), fftypes.Now()) assert.NoError(t, err) - err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusTracking, apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"txhash":"0x12345"}`), nil) + err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusTracking, apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"txhash":"0x12345"}`), nil, fftypes.Now()) assert.NoError(t, err) // Finally the update - do a comprehensive one diff --git a/internal/persistence/postgres/txhistory.go b/internal/persistence/postgres/txhistory.go index e791b747..73904466 100644 --- a/internal/persistence/postgres/txhistory.go +++ b/internal/persistence/postgres/txhistory.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -95,9 +95,9 @@ func (p *sqlPersistence) ListTransactionHistory(ctx context.Context, txID string return p.txHistory.GetMany(ctx, filter.Condition(filter.Builder().Eq("transaction", txID))) } -func (p *sqlPersistence) AddSubStatusAction(ctx context.Context, txID string, subStatus apitypes.TxSubStatus, action apitypes.TxAction, info *fftypes.JSONAny, errInfo *fftypes.JSONAny) error { +func (p *sqlPersistence) AddSubStatusAction(ctx context.Context, txID string, subStatus apitypes.TxSubStatus, action apitypes.TxAction, info *fftypes.JSONAny, errInfo *fftypes.JSONAny, actionOccurred *fftypes.FFTime) error { // Dispatch to TX writer - now := fftypes.Now() + op := newTransactionOperation(txID) op.historyRecord = &apitypes.TXHistoryRecord{ ID: fftypes.NewUUID(), @@ -105,15 +105,15 @@ func (p *sqlPersistence) AddSubStatusAction(ctx context.Context, txID string, su SubStatus: subStatus, TxHistoryActionEntry: apitypes.TxHistoryActionEntry{ OccurrenceCount: 1, - Time: now, - LastOccurrence: now, + Time: actionOccurred, + LastOccurrence: actionOccurred, Action: action, LastInfo: persistence.JSONOrString(info), // guard against bad JSON LastError: persistence.JSONOrString(errInfo), // guard against bad JSON }, } if errInfo != nil { - op.historyRecord.LastErrorTime = fftypes.Now() + op.historyRecord.LastErrorTime = actionOccurred } p.writer.queue(ctx, op) return nil // completely async diff --git a/internal/persistence/postgres/txhistory_test.go b/internal/persistence/postgres/txhistory_test.go index 77cc7cc4..a56d697a 100644 --- a/internal/persistence/postgres/txhistory_test.go +++ b/internal/persistence/postgres/txhistory_test.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -58,37 +58,37 @@ func TestTXHistoryCompressionPSQL(t *testing.T) { // Add a bunch of simulated status entries, which are good for compression err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, - fftypes.JSONAnyPtr(`{"nonce":"11111"}`), nil) + fftypes.JSONAnyPtr(`{"nonce":"11111"}`), nil, fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, - nil, fftypes.JSONAnyPtr(`"failed to submit 111"`)) + nil, fftypes.JSONAnyPtr(`"failed to submit 111"`), fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, - nil, fftypes.JSONAnyPtr(`"failed to submit 222"`)) + nil, fftypes.JSONAnyPtr(`"failed to submit 222"`), fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionSubmitTransaction, - nil, fftypes.JSONAnyPtr(`"failed to submit 333"`)) + nil, fftypes.JSONAnyPtr(`"failed to submit 333"`), fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusTracking, apitypes.TxActionSubmitTransaction, - fftypes.JSONAnyPtr(`{"transactionHash":"0x12345"}`), nil) + fftypes.JSONAnyPtr(`{"transactionHash":"0x12345"}`), nil, fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusTracking, apitypes.TxActionReceiveReceipt, - fftypes.JSONAnyPtr(`{"transactionHash":"0x111111","blockNumber":"11111"}`), nil) + fftypes.JSONAnyPtr(`{"transactionHash":"0x111111","blockNumber":"11111"}`), nil, fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusTracking, apitypes.TxActionReceiveReceipt, - fftypes.JSONAnyPtr(`{"transactionHash":"0x222222","blockNumber":"22222"}`), nil) + fftypes.JSONAnyPtr(`{"transactionHash":"0x222222","blockNumber":"22222"}`), nil, fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusTracking, apitypes.TxActionReceiveReceipt, - fftypes.JSONAnyPtr(`{"transactionHash":"0x333333","blockNumber":"33333"}`), nil) + fftypes.JSONAnyPtr(`{"transactionHash":"0x333333","blockNumber":"33333"}`), nil, fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusTracking, apitypes.TxActionTimeout, - nil, fftypes.JSONAnyPtr(`"some error 444"`)) + nil, fftypes.JSONAnyPtr(`"some error 444"`), fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusTracking, apitypes.TxActionTimeout, - nil, fftypes.JSONAnyPtr(`"some error 555"`)) + nil, fftypes.JSONAnyPtr(`"some error 555"`), fftypes.Now()) assert.NoError(t, err) err = p.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusConfirmed, apitypes.TxActionConfirmTransaction, - fftypes.JSONAnyPtr(`{"confirmed":true}`), nil) + fftypes.JSONAnyPtr(`{"confirmed":true}`), nil, fftypes.Now()) assert.NoError(t, err) // Flush with a TX update diff --git a/mocks/ffcapimocks/api.go b/mocks/ffcapimocks/api.go index 32aaf476..2e9a88c4 100644 --- a/mocks/ffcapimocks/api.go +++ b/mocks/ffcapimocks/api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package ffcapimocks diff --git a/mocks/metricsmocks/transaction_handler_metrics.go b/mocks/metricsmocks/transaction_handler_metrics.go index d9709e89..02eec37c 100644 --- a/mocks/metricsmocks/transaction_handler_metrics.go +++ b/mocks/metricsmocks/transaction_handler_metrics.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package metricsmocks diff --git a/mocks/persistencemocks/persistence.go b/mocks/persistencemocks/persistence.go index 2462d59c..28304ab8 100644 --- a/mocks/persistencemocks/persistence.go +++ b/mocks/persistencemocks/persistence.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package persistencemocks @@ -23,17 +23,17 @@ type Persistence struct { mock.Mock } -// AddSubStatusAction provides a mock function with given fields: ctx, txID, subStatus, action, info, err -func (_m *Persistence) AddSubStatusAction(ctx context.Context, txID string, subStatus apitypes.TxSubStatus, action apitypes.TxAction, info *fftypes.JSONAny, err *fftypes.JSONAny) error { - ret := _m.Called(ctx, txID, subStatus, action, info, err) +// AddSubStatusAction provides a mock function with given fields: ctx, txID, subStatus, action, info, err, actionOccurred +func (_m *Persistence) AddSubStatusAction(ctx context.Context, txID string, subStatus apitypes.TxSubStatus, action apitypes.TxAction, info *fftypes.JSONAny, err *fftypes.JSONAny, actionOccurred *fftypes.FFTime) error { + ret := _m.Called(ctx, txID, subStatus, action, info, err, actionOccurred) if len(ret) == 0 { panic("no return value specified for AddSubStatusAction") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, apitypes.TxSubStatus, apitypes.TxAction, *fftypes.JSONAny, *fftypes.JSONAny) error); ok { - r0 = rf(ctx, txID, subStatus, action, info, err) + if rf, ok := ret.Get(0).(func(context.Context, string, apitypes.TxSubStatus, apitypes.TxAction, *fftypes.JSONAny, *fftypes.JSONAny, *fftypes.FFTime) error); ok { + r0 = rf(ctx, txID, subStatus, action, info, err, actionOccurred) } else { r0 = ret.Error(0) } diff --git a/mocks/persistencemocks/rich_query.go b/mocks/persistencemocks/rich_query.go index eda54d70..be8bec30 100644 --- a/mocks/persistencemocks/rich_query.go +++ b/mocks/persistencemocks/rich_query.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package persistencemocks diff --git a/mocks/persistencemocks/transaction_persistence.go b/mocks/persistencemocks/transaction_persistence.go index 9e15c8e8..cb0570f7 100644 --- a/mocks/persistencemocks/transaction_persistence.go +++ b/mocks/persistencemocks/transaction_persistence.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package persistencemocks diff --git a/mocks/txhandlermocks/managed_tx_event_handler.go b/mocks/txhandlermocks/managed_tx_event_handler.go index a041c8f2..9b124788 100644 --- a/mocks/txhandlermocks/managed_tx_event_handler.go +++ b/mocks/txhandlermocks/managed_tx_event_handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package txhandlermocks diff --git a/mocks/txhandlermocks/transaction_handler.go b/mocks/txhandlermocks/transaction_handler.go index 0fe285fd..6034ad01 100644 --- a/mocks/txhandlermocks/transaction_handler.go +++ b/mocks/txhandlermocks/transaction_handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package txhandlermocks diff --git a/mocks/wsmocks/web_socket_channels.go b/mocks/wsmocks/web_socket_channels.go index 36ed8f9b..ebc2d54e 100644 --- a/mocks/wsmocks/web_socket_channels.go +++ b/mocks/wsmocks/web_socket_channels.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package wsmocks diff --git a/mocks/wsmocks/web_socket_server.go b/mocks/wsmocks/web_socket_server.go index 36866545..3cb6c0f3 100644 --- a/mocks/wsmocks/web_socket_server.go +++ b/mocks/wsmocks/web_socket_server.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package wsmocks diff --git a/pkg/txhandler/simple/policyloop.go b/pkg/txhandler/simple/policyloop.go index bc52491e..46e533c5 100644 --- a/pkg/txhandler/simple/policyloop.go +++ b/pkg/txhandler/simple/policyloop.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -125,7 +125,7 @@ func (sth *simpleTransactionHandler) updateInflightSet(ctx context.Context) bool } var additional []*apitypes.ManagedTX // We retry the get from persistence indefinitely (until the context cancels) - err := sth.retry.Do(ctx, "get pending transactions", func(attempt int) (retry bool, err error) { + err := sth.retry.Do(ctx, "get pending transactions", func(_ int) (retry bool, err error) { additional, err = sth.toolkit.TXPersistence.ListTransactionsPending(ctx, after, spaces, 0) return true, err }) @@ -275,7 +275,7 @@ func (sth *simpleTransactionHandler) pendingToRunContext(baseCtx context.Context if err := sth.toolkit.TXPersistence.SetTransactionReceipt(ctx, mtx.ID, ctx.Receipt); err != nil { return nil, err } - ctx.AddSubStatusAction(apitypes.TxActionReceiveReceipt, fftypes.JSONAnyPtr(`{"protocolId":"`+ctx.Receipt.ProtocolID+`"}`), nil) + ctx.AddSubStatusAction(apitypes.TxActionReceiveReceipt, fftypes.JSONAnyPtr(`{"protocolId":"`+ctx.Receipt.ProtocolID+`"}`), nil, fftypes.Now()) sth.incTransactionOperationCounter(ctx, pending.mtx.Namespace(ctx), "received_receipt") // Clear the notification (as long as no other came through) @@ -292,7 +292,7 @@ func (sth *simpleTransactionHandler) pendingToRunContext(baseCtx context.Context return nil, err } if ctx.Confirmed { - ctx.AddSubStatusAction(apitypes.TxActionConfirmTransaction, nil, nil) + ctx.AddSubStatusAction(apitypes.TxActionConfirmTransaction, nil, nil, fftypes.Now()) ctx.SetSubStatus(apitypes.TxSubStatusConfirmed) } diff --git a/pkg/txhandler/simple/policyloop_test.go b/pkg/txhandler/simple/policyloop_test.go index d134c917..c7412bfa 100644 --- a/pkg/txhandler/simple/policyloop_test.go +++ b/pkg/txhandler/simple/policyloop_test.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -785,7 +785,7 @@ func TestExecPolicyGetTxFail(t *testing.T) { mp := sth.toolkit.TXPersistence.(*persistencemocks.Persistence) mp.On("InsertTransactionWithNextNonce", sth.ctx, mock.Anything, mock.Anything).Return(nil, nil).Once() - mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything).Return(nil) + mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything, mock.Anything).Return(nil) tx := sendSampleTX(t, sth, "0xaaaaa", 12345, "") mp.On("GetTransactionByID", sth.ctx, tx.ID).Return(nil, fmt.Errorf("pop")) @@ -819,7 +819,7 @@ func TestExecPolicyDeleteFail(t *testing.T) { mp := sth.toolkit.TXPersistence.(*persistencemocks.Persistence) mp.On("InsertTransactionWithNextNonce", sth.ctx, mock.Anything, mock.Anything).Return(nil, nil).Once() - mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything).Return(nil) + mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything, mock.Anything).Return(nil) tx := sendSampleTX(t, sth, "0xaaaaa", 12345, "") mp.On("GetTransactionByID", sth.ctx, tx.ID).Return(tx, nil) mp.On("DeleteTransaction", mock.Anything, tx.ID).Return(fmt.Errorf("pop")) @@ -864,7 +864,7 @@ func TestExecPolicyDeleteInflightSync(t *testing.T) { sth.toolkit.EventHandler = eh mp := sth.toolkit.TXPersistence.(*persistencemocks.Persistence) mp.On("InsertTransactionWithNextNonce", sth.ctx, mock.Anything, mock.Anything).Return(nil, nil).Once() - mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything).Return(nil) + mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything, mock.Anything).Return(nil) tx := sendSampleTX(t, sth, "0xaaaaa", 12345, "") sth.inflight = []*pendingState{{mtx: tx}} mp.On("DeleteTransaction", mock.Anything, tx.ID).Return(nil) @@ -911,7 +911,7 @@ func TestExecPolicySuspendInflightSync(t *testing.T) { sth.toolkit.EventHandler = eh mp := sth.toolkit.TXPersistence.(*persistencemocks.Persistence) mp.On("InsertTransactionWithNextNonce", sth.ctx, mock.Anything, mock.Anything).Return(nil, nil).Once() - mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything).Return(nil) + mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything, mock.Anything).Return(nil) tx := sendSampleTX(t, sth, "0xaaaaa", 12345, "") sth.inflight = []*pendingState{{mtx: tx}} mp.On("UpdateTransaction", mock.AnythingOfType("*simple.RunContext"), tx.ID, mock.MatchedBy(func(updates *apitypes.TXUpdates) bool { @@ -960,7 +960,7 @@ func TestExecPolicyResumeSync(t *testing.T) { sth.toolkit.EventHandler = eh mp := sth.toolkit.TXPersistence.(*persistencemocks.Persistence) mp.On("InsertTransactionWithNextNonce", sth.ctx, mock.Anything, mock.Anything).Return(nil, nil).Once() - mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything).Return(nil) + mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything, mock.Anything).Return(nil) tx := sendSampleTX(t, sth, "0xaaaaa", 12345, "") tx.Status = apitypes.TxStatusSuspended mp.On("UpdateTransaction", mock.AnythingOfType("*simple.RunContext"), tx.ID, mock.MatchedBy(func(updates *apitypes.TXUpdates) bool { @@ -1230,7 +1230,7 @@ func TestExecPolicyDeleteNotFound(t *testing.T) { sth.Init(sth.ctx, tk) mp := sth.toolkit.TXPersistence.(*persistencemocks.Persistence) mp.On("InsertTransactionWithNextNonce", sth.ctx, mock.Anything, mock.Anything).Return(nil, nil).Once() - mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything).Return(nil) + mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything, mock.Anything).Return(nil) tx := sendSampleTX(t, sth, "0xaaaaa", 12345, "") sth.inflight = []*pendingState{{mtx: tx}} mp.On("GetTransactionByID", sth.ctx, "bad-id").Return(nil, nil) @@ -1263,7 +1263,7 @@ func TestBadTransactionAPIRequest(t *testing.T) { sth.Init(sth.ctx, tk) mp := sth.toolkit.TXPersistence.(*persistencemocks.Persistence) mp.On("InsertTransactionWithNextNonce", sth.ctx, mock.Anything, mock.Anything).Return(nil, nil).Once() - mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything).Return(nil) + mp.On("AddSubStatusAction", sth.ctx, mock.Anything, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, mock.Anything, mock.Anything, mock.Anything).Return(nil) tx := sendSampleTX(t, sth, "0xaaaaa", 12345, "") sth.inflight = []*pendingState{{mtx: tx}} @@ -1460,7 +1460,7 @@ func TestUpdateFailHistory(t *testing.T) { mp := sth.toolkit.TXPersistence.(*persistencemocks.Persistence) txID := fftypes.NewUUID().String() - mp.On("AddSubStatusAction", mock.AnythingOfType("*simple.RunContext"), txID, mock.Anything, mock.Anything, (*fftypes.JSONAny)(nil), (*fftypes.JSONAny)(nil)).Return(fmt.Errorf("pop")) + mp.On("AddSubStatusAction", mock.AnythingOfType("*simple.RunContext"), txID, mock.Anything, mock.Anything, (*fftypes.JSONAny)(nil), (*fftypes.JSONAny)(nil), mock.Anything).Return(fmt.Errorf("pop")) rc := &RunContext{ Context: context.Background(), @@ -1468,7 +1468,7 @@ func TestUpdateFailHistory(t *testing.T) { ID: txID, }, } - rc.AddSubStatusAction(apitypes.TxActionAssignNonce, nil, nil) + rc.AddSubStatusAction(apitypes.TxActionAssignNonce, nil, nil, fftypes.Now()) err = sth.flushChanges(rc, &pendingState{}, true) assert.Regexp(t, "pop", err) diff --git a/pkg/txhandler/simple/simple_transaction_handler.go b/pkg/txhandler/simple/simple_transaction_handler.go index 091fa0ac..520ba9bb 100644 --- a/pkg/txhandler/simple/simple_transaction_handler.go +++ b/pkg/txhandler/simple/simple_transaction_handler.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -81,10 +81,10 @@ func (ctx *RunContext) SetSubStatus(subStatus apitypes.TxSubStatus) { ctx.SubStatus = subStatus } -func (ctx *RunContext) AddSubStatusAction(action apitypes.TxAction, info *fftypes.JSONAny, err *fftypes.JSONAny) { +func (ctx *RunContext) AddSubStatusAction(action apitypes.TxAction, info *fftypes.JSONAny, err *fftypes.JSONAny, actionOccurred *fftypes.FFTime) { subStatus := ctx.SubStatus // capture at time of action ctx.HistoryUpdates = append(ctx.HistoryUpdates, func(p txhandler.TransactionHistoryPersistence) error { - return p.AddSubStatusAction(ctx, ctx.TX.ID, subStatus, action, info, err) + return p.AddSubStatusAction(ctx, ctx.TX.ID, subStatus, action, info, err, actionOccurred) }) } @@ -332,7 +332,7 @@ func (sth *simpleTransactionHandler) createManagedTx(ctx context.Context, txID s return nextNonceRes.Nonce.Uint64(), nil }) if err == nil { - err = sth.toolkit.TXHistory.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, fftypes.JSONAnyPtr(`{"nonce":"`+mtx.Nonce.String()+`"}`), nil) + err = sth.toolkit.TXHistory.AddSubStatusAction(ctx, txID, apitypes.TxSubStatusReceived, apitypes.TxActionAssignNonce, fftypes.JSONAnyPtr(`{"nonce":"`+mtx.Nonce.String()+`"}`), nil, fftypes.Now()) } if err != nil { return nil, err @@ -348,10 +348,10 @@ func (sth *simpleTransactionHandler) submitTX(ctx *RunContext) (reason ffcapi.Er mtx := ctx.TX mtx.GasPrice, err = sth.getGasPrice(ctx, sth.toolkit.Connector) if err != nil { - ctx.AddSubStatusAction(apitypes.TxActionRetrieveGasPrice, nil, fftypes.JSONAnyPtr(`{"error":"`+err.Error()+`"}`)) + ctx.AddSubStatusAction(apitypes.TxActionRetrieveGasPrice, nil, fftypes.JSONAnyPtr(`{"error":"`+err.Error()+`"}`), fftypes.Now()) return "", err } - ctx.AddSubStatusAction(apitypes.TxActionRetrieveGasPrice, fftypes.JSONAnyPtr(`{"gasPrice":`+string(*mtx.GasPrice)+`}`), nil) + ctx.AddSubStatusAction(apitypes.TxActionRetrieveGasPrice, fftypes.JSONAnyPtr(`{"gasPrice":`+string(*mtx.GasPrice)+`}`), nil, fftypes.Now()) sendTX := &ffcapi.TransactionSendRequest{ TransactionHeaders: mtx.TransactionHeaders, @@ -366,7 +366,7 @@ func (sth *simpleTransactionHandler) submitTX(ctx *RunContext) (reason ffcapi.Er sth.incTransactionOperationCounter(ctx, mtx.Namespace(ctx), "transaction_submission") sth.recordTransactionOperationDuration(ctx, mtx.Namespace(ctx), "transaction_submission", time.Since(transactionSendStartTime).Seconds()) if err == nil { - ctx.AddSubStatusAction(apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"reason":"`+string(reason)+`"}`), nil) + ctx.AddSubStatusAction(apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"reason":"`+string(reason)+`"}`), nil, fftypes.Now()) mtx.TransactionHash = res.TransactionHash mtx.LastSubmit = fftypes.Now() // Need to persist back as we've successfully submitted @@ -375,7 +375,7 @@ func (sth *simpleTransactionHandler) submitTX(ctx *RunContext) (reason ffcapi.Er ctx.TXUpdates.LastSubmit = mtx.LastSubmit ctx.TXUpdates.GasPrice = mtx.GasPrice } else { - ctx.AddSubStatusAction(apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"reason":"`+string(reason)+`"}`), fftypes.JSONAnyPtr(`{"error":"`+err.Error()+`"}`)) + ctx.AddSubStatusAction(apitypes.TxActionSubmitTransaction, fftypes.JSONAnyPtr(`{"reason":"`+string(reason)+`"}`), fftypes.JSONAnyPtr(`{"error":"`+err.Error()+`"}`), fftypes.Now()) // We have some simple rules for handling reasons from the connector, which could be enhanced by extending the connector. switch reason { case ffcapi.ErrorKnownTransaction, ffcapi.ErrorReasonNonceTooLow: @@ -433,7 +433,7 @@ func (sth *simpleTransactionHandler) processTransaction(ctx *RunContext) (err er ctx.UpdatedInfo = true ctx.Info.LastWarnTime = now // We do a resubmit at this point - as it might no longer be in the TX pool - ctx.AddSubStatusAction(apitypes.TxActionTimeout, nil, nil) + ctx.AddSubStatusAction(apitypes.TxActionTimeout, nil, nil, fftypes.Now()) ctx.SetSubStatus(apitypes.TxSubStatusStale) if reason, err := sth.submitTX(ctx); err != nil { if reason != ffcapi.ErrorKnownTransaction { diff --git a/pkg/txhandler/txhandler.go b/pkg/txhandler/txhandler.go index 6ff70c21..5802456f 100644 --- a/pkg/txhandler/txhandler.go +++ b/pkg/txhandler/txhandler.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -70,7 +70,7 @@ type RichQuery interface { } type TransactionHistoryPersistence interface { - AddSubStatusAction(ctx context.Context, txID string, subStatus apitypes.TxSubStatus, action apitypes.TxAction, info *fftypes.JSONAny, err *fftypes.JSONAny) error + AddSubStatusAction(ctx context.Context, txID string, subStatus apitypes.TxSubStatus, action apitypes.TxAction, info *fftypes.JSONAny, err *fftypes.JSONAny, actionOccurred *fftypes.FFTime) error } type TransactionMetrics interface {