From 990627195da9f2e45fa29669cb26a3534642d8fb Mon Sep 17 00:00:00 2001 From: Nate Maninger Date: Wed, 21 Feb 2024 15:33:19 -0800 Subject: [PATCH 1/2] contracts: show alerts for failed resolutions --- host/contracts/actions.go | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/host/contracts/actions.go b/host/contracts/actions.go index f2412e77..e10dea53 100644 --- a/host/contracts/actions.go +++ b/host/contracts/actions.go @@ -10,7 +10,6 @@ import ( "go.sia.tech/core/types" "go.sia.tech/hostd/alerts" "go.uber.org/zap" - "lukechampine.com/frand" ) // An action determines what lifecycle event should be performed on a contract. @@ -178,6 +177,17 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height sp, err := cm.buildStorageProof(contract.Revision.ParentID, contract.Revision.Filesize, leafIndex, log.Named("buildStorageProof")) if err != nil { log.Error("failed to build storage proof", zap.Error(err)) + cm.alerts.Register(alerts.Alert{ + ID: types.Hash256(id), + Severity: alerts.SeverityError, + Message: "Failed to build storage proof", + Data: map[string]any{ + "contractID": id, + "blockHeight": height, + "error": err.Error(), + }, + Timestamp: time.Now(), + }) return } @@ -199,6 +209,17 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height intermediateToSign, discard, err := cm.wallet.FundTransaction(&resolutionTxnSet[0], fee) if err != nil { log.Error("failed to fund resolution transaction", zap.Error(err)) + cm.alerts.Register(alerts.Alert{ + ID: types.Hash256(id), + Severity: alerts.SeverityError, + Message: "Failed to fund resolution transaction", + Data: map[string]any{ + "contractID": id, + "blockHeight": height, + "error": err.Error(), + }, + Timestamp: time.Now(), + }) return } defer discard() @@ -219,8 +240,20 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height } else if err := cm.tpool.AcceptTransactionSet(resolutionTxnSet); err != nil { // broadcast the transaction set buf, _ := json.Marshal(resolutionTxnSet) log.Error("failed to broadcast resolution transaction set", zap.Error(err), zap.ByteString("transactionSet", buf)) + cm.alerts.Register(alerts.Alert{ + ID: types.Hash256(id), + Severity: alerts.SeverityError, + Message: "Failed to broadcast resolution transaction", + Data: map[string]any{ + "contractID": id, + "blockHeight": height, + "error": err.Error(), + }, + Timestamp: time.Now(), + }) return } + cm.alerts.Dismiss(types.Hash256(id)) // dismiss any previous failure alerts log.Info("broadcast storage proof", zap.String("transactionID", resolutionTxnSet[1].ID().String()), zap.Duration("elapsed", time.Since(start))) case ActionReject: if err := cm.store.ExpireContract(id, ContractStatusRejected); err != nil { @@ -255,8 +288,8 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height log.Error("failed to set contract status", zap.Error(err)) } cm.alerts.Register(alerts.Alert{ - ID: frand.Entropy256(), - Severity: alerts.SeverityWarning, + ID: types.Hash256(id), + Severity: alerts.SeverityError, Message: "Contract failed without storage proof", Data: map[string]any{ "contractID": id, From b35ba931c7865a71b6b1cb180b46d354982f4649 Mon Sep 17 00:00:00 2001 From: Nate Maninger Date: Fri, 23 Feb 2024 07:58:37 -0800 Subject: [PATCH 2/2] contracts: add resolution alert helper --- host/contracts/actions.go | 66 ++++++++++++++------------------------- 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/host/contracts/actions.go b/host/contracts/actions.go index e10dea53..74f0706e 100644 --- a/host/contracts/actions.go +++ b/host/contracts/actions.go @@ -98,6 +98,25 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height start := time.Now() cs := cm.chain.TipState() + // helper to register a contract alert + registerContractAlert := func(severity alerts.Severity, message string, err error) { + data := map[string]any{ + "contractID": id, + "blockHeight": height, + } + if err != nil { + data["error"] = err.Error() + } + + cm.alerts.Register(alerts.Alert{ + ID: types.Hash256(id), + Severity: severity, + Message: message, + Data: data, + Timestamp: time.Now(), + }) + } + switch action { case ActionBroadcastFormation: if (height-contract.NegotiationHeight)%3 != 0 { @@ -177,17 +196,7 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height sp, err := cm.buildStorageProof(contract.Revision.ParentID, contract.Revision.Filesize, leafIndex, log.Named("buildStorageProof")) if err != nil { log.Error("failed to build storage proof", zap.Error(err)) - cm.alerts.Register(alerts.Alert{ - ID: types.Hash256(id), - Severity: alerts.SeverityError, - Message: "Failed to build storage proof", - Data: map[string]any{ - "contractID": id, - "blockHeight": height, - "error": err.Error(), - }, - Timestamp: time.Now(), - }) + registerContractAlert(alerts.SeverityError, "Failed to build storage proof", err) return } @@ -209,17 +218,7 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height intermediateToSign, discard, err := cm.wallet.FundTransaction(&resolutionTxnSet[0], fee) if err != nil { log.Error("failed to fund resolution transaction", zap.Error(err)) - cm.alerts.Register(alerts.Alert{ - ID: types.Hash256(id), - Severity: alerts.SeverityError, - Message: "Failed to fund resolution transaction", - Data: map[string]any{ - "contractID": id, - "blockHeight": height, - "error": err.Error(), - }, - Timestamp: time.Now(), - }) + registerContractAlert(alerts.SeverityError, "Failed to fund resolution transaction", err) return } defer discard() @@ -240,17 +239,7 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height } else if err := cm.tpool.AcceptTransactionSet(resolutionTxnSet); err != nil { // broadcast the transaction set buf, _ := json.Marshal(resolutionTxnSet) log.Error("failed to broadcast resolution transaction set", zap.Error(err), zap.ByteString("transactionSet", buf)) - cm.alerts.Register(alerts.Alert{ - ID: types.Hash256(id), - Severity: alerts.SeverityError, - Message: "Failed to broadcast resolution transaction", - Data: map[string]any{ - "contractID": id, - "blockHeight": height, - "error": err.Error(), - }, - Timestamp: time.Now(), - }) + registerContractAlert(alerts.SeverityError, "Failed to broadcast resolution transaction set", err) return } cm.alerts.Dismiss(types.Hash256(id)) // dismiss any previous failure alerts @@ -287,16 +276,7 @@ func (cm *ContractManager) handleContractAction(id types.FileContractID, height if err := cm.store.ExpireContract(id, ContractStatusFailed); err != nil { log.Error("failed to set contract status", zap.Error(err)) } - cm.alerts.Register(alerts.Alert{ - ID: types.Hash256(id), - Severity: alerts.SeverityError, - Message: "Contract failed without storage proof", - Data: map[string]any{ - "contractID": id, - "blockHeight": height, - }, - Timestamp: time.Now(), - }) + registerContractAlert(alerts.SeverityError, "Contract failed without storage proof", nil) log.Error("contract failed, revenue lost", zap.Uint64("windowStart", contract.Revision.WindowStart), zap.Uint64("windowEnd", contract.Revision.WindowEnd), zap.String("validPayout", validPayout.ExactString()), zap.String("missedPayout", missedPayout.ExactString())) default: log.Panic("unrecognized contract state", zap.Stack("stack"), zap.String("validPayout", validPayout.ExactString()), zap.String("missedPayout", missedPayout.ExactString()), zap.Uint64("resolutionHeight", contract.ResolutionHeight), zap.Bool("formationConfirmed", contract.FormationConfirmed))