From 2d49cc47a27d0f6c7d866b0bf242688bfc9e92ca Mon Sep 17 00:00:00 2001 From: James Walker Date: Wed, 11 Sep 2024 13:32:48 -0400 Subject: [PATCH] feat: allow address targeting (#307) * feat: allow address targeting * feat: add JobOfferCancelled handling * fix: pr review comments --------- Co-authored-by: Narb <29411347+narbs91@users.noreply.github.com> --- cmd/lilypad/run.go | 3 +++ pkg/data/enums.go | 3 ++- pkg/data/types.go | 7 +++++++ pkg/data/utils.go | 2 +- pkg/jobcreator/jobcreator.go | 2 ++ pkg/jobcreator/run.go | 5 +++++ pkg/jobcreator/utils.go | 1 + pkg/options/job-creator.go | 7 +++++++ pkg/options/target.go | 17 +++++++++++++++++ pkg/solver/controller.go | 2 +- pkg/solver/matcher.go | 27 +++++++++++++++++++++++++++ pkg/solver/store/memory/store.go | 12 ++++++++++++ pkg/solver/store/types.go | 1 + 13 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 pkg/options/target.go diff --git a/cmd/lilypad/run.go b/cmd/lilypad/run.go index 1fd460e7..9e9e62a4 100644 --- a/cmd/lilypad/run.go +++ b/cmd/lilypad/run.go @@ -116,6 +116,9 @@ func runJob(cmd *cobra.Command, options jobcreator.JobCreatorOptions) error { case "ResultsRejected": desc = "Results rejected! Getting refund..." emoji = "🙀" + case "JobOfferCancelled": + desc = "Job cancelled..." + emoji = "😭" default: desc = st emoji = "🌟" diff --git a/pkg/data/enums.go b/pkg/data/enums.go index 7f5d5ec1..0f94df3d 100644 --- a/pkg/data/enums.go +++ b/pkg/data/enums.go @@ -26,6 +26,7 @@ var AgreementState = []string{ "TimeoutSubmitResults", "TimeoutJudgeResults", "TimeoutMediateResults", + "JobOfferCancelled", } // PaymentReason corresponds to PaymentReason in TypeScript @@ -83,7 +84,7 @@ func IsActiveAgreementState(itemType uint8) bool { } func IsTerminalAgreementState(itemType uint8) bool { - return itemType == GetAgreementStateIndex("ResultsAccepted") || itemType == GetAgreementStateIndex("MediationAccepted") || itemType == GetAgreementStateIndex("MediationRejected") + return itemType == GetAgreementStateIndex("JobOfferCancelled") || itemType == GetAgreementStateIndex("ResultsAccepted") || itemType == GetAgreementStateIndex("MediationAccepted") || itemType == GetAgreementStateIndex("MediationRejected") } // GetPaymentReason corresponds to getPaymentReason in TypeScript diff --git a/pkg/data/types.go b/pkg/data/types.go index 007fa2ae..7597de80 100644 --- a/pkg/data/types.go +++ b/pkg/data/types.go @@ -88,6 +88,10 @@ type ServiceConfig struct { APIHost string `json:"api_host" toml:"api_host"` } +type TargetConfig struct { + Address string `json:"address" toml:"address"` +} + // posted to the solver by a job creator type JobOffer struct { // this is the cid of the job offer where ID is set to empty string @@ -114,6 +118,9 @@ type JobOffer struct { // which parties are trusted by the job creator Services ServiceConfig `json:"trusted_parties"` + + // which node(s) (if any) to target + Target TargetConfig `json:"target"` } // this is what the solver keeps track of so we can know diff --git a/pkg/data/utils.go b/pkg/data/utils.go index 31f16365..f55a1e9d 100644 --- a/pkg/data/utils.go +++ b/pkg/data/utils.go @@ -5,8 +5,8 @@ import ( "fmt" "math/big" - "github.com/lilypad-tech/lilypad/pkg/web3/bindings/controller" "github.com/ethereum/go-ethereum/common" + "github.com/lilypad-tech/lilypad/pkg/web3/bindings/controller" mdag "github.com/ipfs/go-merkledag" ) diff --git a/pkg/jobcreator/jobcreator.go b/pkg/jobcreator/jobcreator.go index cde215e3..984af3f8 100644 --- a/pkg/jobcreator/jobcreator.go +++ b/pkg/jobcreator/jobcreator.go @@ -31,6 +31,8 @@ type JobCreatorOfferOptions struct { Inputs map[string]string // which mediators and directories this RP will trust Services data.ServiceConfig + // which node(s) (if any) to target + Target data.TargetConfig } type JobCreatorOptions struct { diff --git a/pkg/jobcreator/run.go b/pkg/jobcreator/run.go index ad9b4cfc..03795571 100644 --- a/pkg/jobcreator/run.go +++ b/pkg/jobcreator/run.go @@ -79,6 +79,11 @@ waitloop: } } + // Check if our job was cancelled + if finalJobOffer.State == data.GetAgreementStateIndex("JobOfferCancelled") { + return nil, fmt.Errorf("job was cancelled") + } + result, err := jobCreatorService.GetResult(finalJobOffer.DealID) if err != nil { return nil, err diff --git a/pkg/jobcreator/utils.go b/pkg/jobcreator/utils.go index 17d05828..8e04888d 100644 --- a/pkg/jobcreator/utils.go +++ b/pkg/jobcreator/utils.go @@ -29,5 +29,6 @@ func getJobOfferFromOptions(options JobCreatorOfferOptions, jobCreatorAddress st Pricing: options.Pricing, Timeouts: options.Timeouts, Services: options.Services, + Target: options.Target, }, nil } diff --git a/pkg/options/job-creator.go b/pkg/options/job-creator.go index 591ae4f9..78382bcf 100644 --- a/pkg/options/job-creator.go +++ b/pkg/options/job-creator.go @@ -55,6 +55,7 @@ func AddJobCreatorOfferCliFlags(cmd *cobra.Command, offerOptions *jobcreator.Job AddTimeoutCliFlags(cmd, &offerOptions.Timeouts) AddModuleCliFlags(cmd, &offerOptions.Module) AddServicesCliFlags(cmd, &offerOptions.Services) + AddTargetCliFlags(cmd, &offerOptions.Target) } func AddJobCreatorCliFlags(cmd *cobra.Command, options *jobcreator.JobCreatorOptions) { @@ -112,6 +113,12 @@ func ProcessJobCreatorOptions(options jobcreator.JobCreatorOptions, args []strin } options.Offer.Services = newServicesOptions + newTargetOptions, err := ProcessTargetOptions(options.Offer.Target) + if err != nil { + return options, err + } + options.Offer.Target = newTargetOptions + return options, CheckJobCreatorOptions(options) } diff --git a/pkg/options/target.go b/pkg/options/target.go new file mode 100644 index 00000000..01e20f6c --- /dev/null +++ b/pkg/options/target.go @@ -0,0 +1,17 @@ +package options + +import ( + "github.com/lilypad-tech/lilypad/pkg/data" + "github.com/spf13/cobra" +) + +func AddTargetCliFlags(cmd *cobra.Command, targetConfig *data.TargetConfig) { + cmd.PersistentFlags().StringVarP( + &targetConfig.Address, "target", "t", targetConfig.Address, + `The address to target (TARGET)`, + ) +} + +func ProcessTargetOptions(options data.TargetConfig) (data.TargetConfig, error) { + return options, nil +} diff --git a/pkg/solver/controller.go b/pkg/solver/controller.go index f49cbb74..70f788e1 100644 --- a/pkg/solver/controller.go +++ b/pkg/solver/controller.go @@ -268,7 +268,7 @@ func (controller *SolverController) registerAsSolver() error { func (controller *SolverController) solve() error { // find out which deals we can make from matching the offers - deals, err := getMatchingDeals(controller.store) + deals, err := getMatchingDeals(controller.store, controller.updateJobOfferState) if err != nil { return err } diff --git a/pkg/solver/matcher.go b/pkg/solver/matcher.go index 2a9015e1..73265b58 100644 --- a/pkg/solver/matcher.go +++ b/pkg/solver/matcher.go @@ -123,6 +123,7 @@ func doOffersMatch( func getMatchingDeals( db store.SolverStore, + updateJobOfferState func(string, string, uint8) (*data.JobOfferContainer, error), ) ([]data.Deal, error) { deals := []data.Deal{} @@ -143,6 +144,32 @@ func getMatchingDeals( // loop over job offers for _, jobOffer := range jobOffers { + // See if our jobOffer targets a specific address. If so, we will create a deal automatically + // with the matcing resourceOffer. + if jobOffer.JobOffer.Target.Address != "" { + resourceOffer, err := db.GetResourceOfferByAddress(jobOffer.JobOffer.Target.Address) + if err != nil { + return nil, err + } + + // We don't have a resource provider for this address + if resourceOffer == nil { + log.Trace(). + Str("job offer", jobOffer.ID). + Str("target address", jobOffer.JobOffer.Target.Address). + Msgf("No resource provider found for address") + updateJobOfferState(jobOffer.ID, "", data.GetAgreementStateIndex("JobOfferCancelled")) + continue + } + + deal, err := data.GetDeal(jobOffer.JobOffer, resourceOffer.ResourceOffer) + if err != nil { + return nil, err + } + deals = append(deals, deal) + continue + } + // loop over resource offers matchingResourceOffers := []data.ResourceOffer{} for _, resourceOffer := range resourceOffers { diff --git a/pkg/solver/store/memory/store.go b/pkg/solver/store/memory/store.go index 293b6562..9faa00f8 100644 --- a/pkg/solver/store/memory/store.go +++ b/pkg/solver/store/memory/store.go @@ -3,6 +3,7 @@ package store import ( "fmt" "os" + "strings" "sync" "github.com/lilypad-tech/lilypad/pkg/data" @@ -197,6 +198,17 @@ func (s *SolverStoreMemory) GetResourceOffer(id string) (*data.ResourceOfferCont return resourceOffer, nil } +func (s *SolverStoreMemory) GetResourceOfferByAddress(address string) (*data.ResourceOfferContainer, error) { + s.mutex.RLock() + defer s.mutex.RUnlock() + for _, resourceOffer := range s.resourceOfferMap { + if strings.ToLower(resourceOffer.ResourceProvider) == strings.ToLower(address) { + return resourceOffer, nil + } + } + return nil, nil +} + func (s *SolverStoreMemory) GetDeal(id string) (*data.DealContainer, error) { s.mutex.RLock() defer s.mutex.RUnlock() diff --git a/pkg/solver/store/types.go b/pkg/solver/store/types.go index 1341366e..fea48522 100644 --- a/pkg/solver/store/types.go +++ b/pkg/solver/store/types.go @@ -53,6 +53,7 @@ type SolverStore interface { GetDeals(query GetDealsQuery) ([]data.DealContainer, error) GetJobOffer(id string) (*data.JobOfferContainer, error) GetResourceOffer(id string) (*data.ResourceOfferContainer, error) + GetResourceOfferByAddress(address string) (*data.ResourceOfferContainer, error) GetDeal(id string) (*data.DealContainer, error) GetResult(id string) (*data.Result, error) GetMatchDecision(resourceOffer string, jobOffer string) (*data.MatchDecision, error)