github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/market/service_proposal.go (about) 1 /* 2 * Copyright (C) 2017 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package market 19 20 import ( 21 "encoding/json" 22 23 validation "github.com/go-ozzo/ozzo-validation" 24 "github.com/mysteriumnetwork/node/p2p/compat" 25 "github.com/mysteriumnetwork/node/utils/validateutil" 26 ) 27 28 const ( 29 proposalFormat = "service-proposal/v3" 30 ) 31 32 // ServiceProposal is top level structure which is presented to marketplace by service provider, and looked up by service consumer 33 // service proposal can be marked as unsupported by deserializer, because of unknown service, payment method, or contact type 34 type ServiceProposal struct { 35 ID int64 `json:"id"` 36 37 // A version number is included in the proposal to allow extensions to the proposal format 38 Format string `json:"format"` 39 40 Compatibility int `json:"compatibility"` 41 42 // Unique identifier of a provider 43 ProviderID string `json:"provider_id"` 44 45 // Type of service type offered 46 ServiceType string `json:"service_type"` 47 48 // Service location 49 Location Location `json:"location"` 50 51 // Communication methods possible 52 Contacts ContactList `json:"contacts"` 53 54 // AccessPolicies represents the access controls for proposal 55 AccessPolicies *[]AccessPolicy `json:"access_policies,omitempty"` 56 57 // Quality represents the service quality. 58 Quality Quality `json:"quality"` 59 } 60 61 // NewProposalOpts optional params for the new proposal creation. 62 type NewProposalOpts struct { 63 Location *Location 64 AccessPolicies []AccessPolicy 65 Contacts []Contact 66 Quality *Quality 67 } 68 69 // NewProposal creates a new proposal. 70 func NewProposal(providerID, serviceType string, opts NewProposalOpts) ServiceProposal { 71 p := ServiceProposal{ 72 Format: proposalFormat, 73 Compatibility: compat.Compatibility, 74 ProviderID: providerID, 75 ServiceType: serviceType, 76 Location: Location{}, 77 Contacts: nil, 78 AccessPolicies: nil, 79 } 80 if loc := opts.Location; loc != nil { 81 p.Location = *loc 82 } 83 if ap := opts.AccessPolicies; ap != nil { 84 p.AccessPolicies = &ap 85 } 86 if c := opts.Contacts; c != nil { 87 p.Contacts = c 88 } 89 if q := opts.Quality; q != nil { 90 p.Quality = *q 91 } 92 return p 93 } 94 95 // Validate validates the proposal. 96 func (proposal *ServiceProposal) Validate() error { 97 return validation.ValidateStruct(proposal, 98 validation.Field(&proposal.Format, validation.Required, validation.By(validateutil.StringEquals(proposalFormat))), 99 validation.Field(&proposal.ProviderID, validation.Required), 100 validation.Field(&proposal.ServiceType, validation.Required), 101 validation.Field(&proposal.Location, validation.Required), 102 validation.Field(&proposal.Contacts, validation.Required), 103 ) 104 } 105 106 // UniqueID returns unique proposal composite ID 107 func (proposal *ServiceProposal) UniqueID() ProposalID { 108 return ProposalID{ 109 ProviderID: proposal.ProviderID, 110 ServiceType: proposal.ServiceType, 111 } 112 } 113 114 // UnmarshalJSON is custom json unmarshaler to dynamically fill in ServiceProposal values 115 func (proposal *ServiceProposal) UnmarshalJSON(data []byte) error { 116 var jsonData struct { 117 ID int64 `json:"id"` 118 Format string `json:"format"` 119 ProviderID string `json:"provider_id"` 120 ServiceType string `json:"service_type"` 121 Compatibility int `json:"compatibility"` 122 Location Location `json:"location"` 123 Contacts *json.RawMessage `json:"contacts"` 124 AccessPolicies *[]AccessPolicy `json:"access_policies,omitempty"` 125 Quality Quality `json:"quality"` 126 } 127 if err := json.Unmarshal(data, &jsonData); err != nil { 128 return err 129 } 130 131 proposal.ID = jsonData.ID 132 proposal.Format = jsonData.Format 133 proposal.ProviderID = jsonData.ProviderID 134 proposal.ServiceType = jsonData.ServiceType 135 proposal.Compatibility = jsonData.Compatibility 136 proposal.Location = jsonData.Location 137 138 // run contact unserializer 139 proposal.Contacts = unserializeContacts(jsonData.Contacts) 140 proposal.AccessPolicies = jsonData.AccessPolicies 141 proposal.Quality = jsonData.Quality 142 143 return nil 144 } 145 146 // IsSupported returns true if this service proposal can be used for connections by service consumer 147 // can be used as a filter to filter out all proposals which are unsupported for any reason 148 func (proposal *ServiceProposal) IsSupported() bool { 149 if _, ok := supportedServices[proposal.ServiceType]; !ok { 150 return false 151 } 152 153 for _, contact := range proposal.Contacts { 154 if _, notSupported := contact.Definition.(UnsupportedContactType); notSupported { 155 continue 156 } 157 //at least one is supported - we are ok 158 return true 159 } 160 161 return false 162 } 163 164 var supportedServices = make(map[string]struct{}) 165 166 // RegisterServiceType registers a supported service type. 167 func RegisterServiceType(serviceType string) { 168 supportedServices[serviceType] = struct{}{} 169 }