package peer import ( "fmt" "strings" "time" "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" ) type PeerRelation int const ( NONE PeerRelation = iota SELF PARTNER BLACKLIST PENDING_PARTNER ) var path = []string{"unknown", "self", "partner", "blacklist", "partner"} func GetRelationPath(str string) int { for i, p := range path { if str == p { return i } } return -1 } func (m PeerRelation) Path() string { return path[m] } func (m PeerRelation) String() string { return strings.ToUpper(path[m]) } func (m PeerRelation) EnumIndex() int { return int(m) } // BehaviorWarning records a single misbehavior observed by a trusted service. type BehaviorWarning struct { At time.Time `json:"at" bson:"at"` ReporterApp string `json:"reporter_app" bson:"reporter_app"` Severity tools.BehaviorSeverity `json:"severity" bson:"severity"` Reason string `json:"reason" bson:"reason"` Evidence string `json:"evidence,omitempty" bson:"evidence,omitempty"` } // Peer is a struct that represents a peer type Peer struct { utils.AbstractObject Verify bool `json:"verify" bson:"verify"` PeerID string `json:"peer_id" bson:"peer_id" validate:"required"` APIUrl string `json:"api_url" bson:"api_url" validate:"required"` // Url is the URL of the peer (base64url) StreamAddress string `json:"stream_address" bson:"stream_address" validate:"required"` // Url is the URL of the peer (base64url) NATSAddress string `json:"nats_address" bson:"nats_address" validate:"required"` WalletAddress string `json:"wallet_address" bson:"wallet_address" validate:"required"` // WalletAddress is the wallet address of the peer PublicKey string `json:"public_key" bson:"public_key" validate:"required"` // PublicKey is the public key of the peer Relation PeerRelation `json:"relation" bson:"relation" default:"0"` ServicesState map[string]int `json:"services_state,omitempty" bson:"services_state,omitempty"` FailedExecution []PeerExecution `json:"failed_execution" bson:"failed_execution"` // FailedExecution is the list of failed executions, to be retried // Trust scoring — maintained by oc-discovery from PEER_BEHAVIOR_EVENT reports. TrustScore float64 `json:"trust_score" bson:"trust_score" default:"100"` BlacklistReason string `json:"blacklist_reason,omitempty" bson:"blacklist_reason,omitempty"` BehaviorWarnings []BehaviorWarning `json:"behavior_warnings,omitempty" bson:"behavior_warnings,omitempty"` } func (ao *Peer) VerifyAuth(callName string, request *tools.APIRequest) bool { return true } // BlacklistThreshold is the trust score below which a peer is auto-blacklisted. const BlacklistThreshold = 20.0 // ApplyBehaviorReport records a misbehavior, deducts the trust penalty, and // returns true when the trust score has fallen below BlacklistThreshold so the // caller can trigger the relation change. func (p *Peer) ApplyBehaviorReport(r tools.PeerBehaviorReport) (shouldBlacklist bool) { p.BehaviorWarnings = append(p.BehaviorWarnings, BehaviorWarning{ At: r.At, ReporterApp: r.ReporterApp, Severity: r.Severity, Reason: r.Reason, Evidence: r.Evidence, }) if p.TrustScore == 0 { p.TrustScore = 100 // initialise if never set } p.TrustScore -= r.Severity.Penalty() if p.TrustScore < 0 { p.TrustScore = 0 } if p.TrustScore <= BlacklistThreshold { p.BlacklistReason = r.Reason return true } return false } // ResetTrust clears all behavior history and resets the trust score to 100. // Must be called when a peer relation is manually set to NONE or PARTNER. func (p *Peer) ResetTrust() { p.TrustScore = 100 p.BlacklistReason = "" p.BehaviorWarnings = nil } // AddExecution adds an execution to the list of failed executions func (ao *Peer) AddExecution(exec PeerExecution) { found := false for _, v := range ao.FailedExecution { // Check if the execution is already in the list if v.Url == exec.Url && v.Method == exec.Method && fmt.Sprint(v.Body) == fmt.Sprint(exec.Body) { found = true break } } if !found { ao.FailedExecution = append(ao.FailedExecution, exec) } } // RemoveExecution removes an execution from the list of failed executions func (ao *Peer) RemoveExecution(exec PeerExecution) { new := []PeerExecution{} for i, v := range ao.FailedExecution { if !(v.Url == exec.Url && v.Method == exec.Method && fmt.Sprint(v.Body) == fmt.Sprint(exec.Body)) { new = append(new, ao.FailedExecution[i]) } } ao.FailedExecution = new } // LaunchPeerExecution launches an execution on a peer func (p *Peer) LaunchPeerExecution(peerID string, dataID string, dt tools.DataType, method tools.METHOD, body interface{}, caller *tools.HTTPCaller) (map[string]interface{}, error) { p.UUID = peerID return cache.LaunchPeerExecution(peerID, dataID, dt, method, body, caller) // Launch the execution on the peer through the cache } func (d *Peer) GetAccessor(request *tools.APIRequest) utils.Accessor { data := NewAccessor(request) // Create a new instance of the accessor return data } func (r *Peer) CanDelete() bool { return false // only draft order can be deleted }