2026-07-15 09:43:33 +08:00

51 lines
1.7 KiB
Go

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
}