~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
240 lines · 6.1 KB Raw History
package market

import (
	"cmp"
	"strings"

	apimeta "k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
)

type ContractRef struct {
	Name string

	UID string
}

type Addressees struct {
	DIDs map[string]bool

	RFPs map[string]ContractRef

	Accepts map[string]ContractRef

	Receipts map[string]ContractRef

	Winners map[string]bool

	Awaiting map[string]bool
}

func newAddressees() *Addressees {
	return &Addressees{DIDs: map[string]bool{}, RFPs: map[string]ContractRef{}, Accepts: map[string]ContractRef{},
		Receipts: map[string]ContractRef{}, Winners: map[string]bool{}, Awaiting: map[string]bool{}}
}

func RequesterContractDone(contract v1alpha1.ComputeContract) bool {
	state := contract.Status.Contract
	phase := cmp.Or(contract.Status.Phase, v1alpha1.ComputeContractPending)
	terminal := phase == v1alpha1.ComputeContractReleased
	if phase == v1alpha1.ComputeContractFailed {
		terminal = state == nil || state.Receipt == nil
		if terminal && contract.Spec.Release && state != nil && state.Accept != nil && state.Accept.URI != "" && state.Winner != nil && state.Winner.DID != "" {
			terminal = false
		}
	}
	if terminal && state != nil && state.Receipt != nil && state.Termination == nil && state.Winner != nil && state.Winner.DID != "" {
		terminal = RequesterTerminationNotReported(contract)
	}
	return terminal
}

const TerminationNotReportedReason = "NotReported"

func RequesterTerminationNotReported(contract v1alpha1.ComputeContract) bool {
	if contract.Status.Phase != v1alpha1.ComputeContractReleased {
		return false
	}
	terminated := apimeta.FindStatusCondition(contract.Status.Conditions, v1alpha1.ComputeContractConditionTerminated)
	return terminated != nil && terminated.Status == metav1.ConditionFalse && terminated.Reason == TerminationNotReportedReason
}

type ContractRefs struct {
	RFP string

	Accept string

	Receipt string

	Winner string
}

func RefsOfContract(state *v1alpha1.NodeClaimContract) ContractRefs {
	var refs ContractRefs
	if state == nil {
		return refs
	}
	refs.RFP = state.RFPURI
	if state.RFP != nil {
		refs.RFP = cmp.Or(state.RFP.URI, refs.RFP)
	}
	refs.Accept = state.AcceptURI
	if state.Accept != nil {
		refs.Accept = cmp.Or(state.Accept.URI, refs.Accept)
	}
	refs.Receipt = state.ReceiptURI
	if state.Receipt != nil {
		refs.Receipt = cmp.Or(state.Receipt.URI, refs.Receipt)
	}
	if state.Bid != nil {
		refs.Winner = ATURIAuthority(state.Bid.URI)
	}
	if refs.Winner == "" && state.Winner != nil {
		refs.Winner = state.Winner.DID
	}
	return refs
}

func (a *Addressees) Add(ref ContractRef, refs ContractRefs, open bool) {
	if refs.RFP != "" && open {
		a.RFPs[refs.RFP] = ref
	}
	if refs.Accept != "" {
		a.Accepts[refs.Accept] = ref
		if refs.Winner != "" {
			a.Winners[refs.Winner] = true
			if refs.Receipt == "" {
				a.Awaiting[refs.Winner] = true
			}
		}
	}
	if refs.Receipt != "" {
		a.Receipts[refs.Receipt] = ref
	}
}

func RequesterContractSettled(contract v1alpha1.ComputeContract) bool {
	if RequesterContractDone(contract) {
		return true
	}
	return contract.Status.Phase == v1alpha1.ComputeContractReleased &&
		apimeta.IsStatusConditionTrue(contract.Status.Conditions, v1alpha1.ComputeContractConditionReleaseAcknowledged)
}

func RequesterContractEnded(contract v1alpha1.ComputeContract) bool {
	return contract.Status.Phase == v1alpha1.ComputeContractReleased || contract.Status.Phase == v1alpha1.ComputeContractFailed
}

func RequesterAddresseesOf(contracts []v1alpha1.ComputeContract, skip func(v1alpha1.ComputeContract) bool) *Addressees {
	a := newAddressees()
	for _, contract := range contracts {
		if skip != nil && skip(contract) {
			continue
		}
		refs := RefsOfContract(contract.Status.Contract)
		a.Add(ContractRef{Name: contract.Name, UID: string(contract.UID)}, refs, skip == nil || refs.Accept == "")
	}
	return a
}

func BidderAddresseesOf(dids ...string) *Addressees {
	a := newAddressees()
	for _, did := range dids {
		if did = strings.TrimSpace(did); strings.HasPrefix(did, "did:") {
			a.DIDs[did] = true
		}
	}
	return a
}

func RequesterConsumes(collection string) bool {
	switch collection {
	case BidNSID, ReceiptNSID, EventNSID:
		return true
	}
	return false
}

func BidderConsumes(collection string) bool {
	switch collection {
	case RFPNSID, AcceptNSID, EventNSID:
		return true
	}
	return false
}

func (a *Addressees) RequesterWants(collection, author string) bool {
	switch collection {
	case BidNSID:
		return len(a.RFPs) > 0
	case ReceiptNSID, EventNSID:
		return a.Winners[author]
	}
	return false
}

func (a *Addressees) RequesterMatch(collection, author string, record map[string]any) (ContractRef, string, bool) {
	switch collection {
	case BidNSID:
		bid, err := ParseBid(record)
		if err != nil {
			return ContractRef{}, "", false
		}
		ref, ok := a.RFPs[bid.RFP.URI]
		return ref, "", ok
	case ReceiptNSID:
		receipt, err := ParseReceipt(record)
		if err != nil {
			return ContractRef{}, "", false
		}
		ref, ok := a.Accepts[receipt.Accept.URI]
		return ref, "", ok
	case EventNSID:
		event, err := ParseEvent(record)
		if err != nil {
			return ContractRef{}, "", false
		}
		if ref, ok := a.Receipts[event.Receipt.URI]; ok {
			return ref, "", true
		}
		if a.Awaiting[author] && ATURIAuthority(event.Receipt.URI) == author && NSIDFromURI(event.Receipt.URI) == ReceiptNSID {
			return ContractRef{}, event.Receipt.URI, false
		}
	}
	return ContractRef{}, "", false
}

func (a *Addressees) ReceiptMatch(record map[string]any) (ContractRef, bool) {
	receipt, err := ParseReceipt(record)
	if err != nil {
		return ContractRef{}, false
	}
	ref, ok := a.Accepts[receipt.Accept.URI]
	return ref, ok
}

func (a *Addressees) own(did string) bool {
	return did != "" && a.DIDs[did]
}

func (a *Addressees) BidderAddressed(collection string, record map[string]any) bool {
	switch collection {
	case RFPNSID:
		return true
	case AcceptNSID:
		accept, err := ParseAccept(record)
		if err != nil {
			return false
		}
		return len(a.DIDs) == 0 || a.own(ATURIAuthority(accept.Bid.URI))
	case EventNSID:
		event, err := ParseEvent(record)
		if err != nil {
			return false
		}
		return len(a.DIDs) == 0 || a.own(ATURIAuthority(event.Receipt.URI))
	}
	return false
}