package resource import ( "crypto/sha256" "encoding/hex" "encoding/json" "sort" ) // CanonicalGiftConfigsJSON produces the immutable semantic bytes stored with a pinned gift entitlement. // A resource can back more than one gift ID, so the promise contains every gift that was sendable at pin time. func CanonicalGiftConfigsJSON(configs []GiftConfig) ([]byte, error) { return json.Marshal(canonicalGiftConfigs(configs)) } func GiftConfigsContentHash(configs []GiftConfig) (string, error) { body, err := CanonicalGiftConfigsJSON(configs) if err != nil { return "", err } sum := sha256.Sum256(body) return hex.EncodeToString(sum[:]), nil } func canonicalGiftConfigs(configs []GiftConfig) []GiftConfig { if len(configs) == 0 { return nil } canonical := append([]GiftConfig(nil), configs...) for index := range canonical { canonical[index].EffectTypes = append([]string(nil), canonical[index].EffectTypes...) sort.Strings(canonical[index].EffectTypes) canonical[index].RegionIDs = append([]int64(nil), canonical[index].RegionIDs...) sort.Slice(canonical[index].RegionIDs, func(left, right int) bool { return canonical[index].RegionIDs[left] < canonical[index].RegionIDs[right] }) canonical[index].Resource.UsageScopes = append([]string(nil), canonical[index].Resource.UsageScopes...) sort.Strings(canonical[index].Resource.UsageScopes) } sort.SliceStable(canonical, func(left, right int) bool { if canonical[left].GiftID != canonical[right].GiftID { return canonical[left].GiftID < canonical[right].GiftID } if canonical[left].ResourceID != canonical[right].ResourceID { return canonical[left].ResourceID < canonical[right].ResourceID } return canonical[left].PriceVersion < canonical[right].PriceVersion }) return canonical }