108 lines
3.5 KiB
Go
108 lines
3.5 KiB
Go
package activity
|
|
|
|
import (
|
|
"bindbox-game/internal/repository/mysql"
|
|
"bindbox-game/internal/repository/mysql/dao"
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"errors"
|
|
)
|
|
|
|
type ActivityCommitmentService struct {
|
|
read *dao.Query
|
|
write *dao.Query
|
|
repo mysql.Repo
|
|
}
|
|
|
|
func NewActivityCommitmentService(read *dao.Query, write *dao.Query, repo mysql.Repo) *ActivityCommitmentService {
|
|
return &ActivityCommitmentService{read: read, write: write, repo: repo}
|
|
}
|
|
|
|
func (s *ActivityCommitmentService) Generate(ctx context.Context, activityID int64) (int32, error) {
|
|
act, err := s.read.Activities.WithContext(ctx).Where(s.read.Activities.ID.Eq(activityID)).First()
|
|
if err != nil || act == nil {
|
|
return 0, errors.New("activity not found")
|
|
}
|
|
var cur int32
|
|
_ = s.repo.GetDbR().Raw("SELECT IFNULL(commitment_state_version,0) FROM activities WHERE id=?", activityID).Scan(&cur)
|
|
seed := make([]byte, 32)
|
|
_, _ = rand.Read(seed)
|
|
seedHash := sha256.Sum256(seed)
|
|
// compute items_root by aggregating all issues' reward slots
|
|
issueIDs := make([]int64, 0)
|
|
issues, _ := s.read.ActivityIssues.WithContext(ctx).ReadDB().Where(s.read.ActivityIssues.ActivityID.Eq(activityID)).Find()
|
|
for _, is := range issues {
|
|
issueIDs = append(issueIDs, is.ID)
|
|
}
|
|
var itemsRoot []byte
|
|
if len(issueIDs) > 0 {
|
|
// fetch rewards per issue and build slots
|
|
slots := make([]int64, 0)
|
|
for _, iid := range issueIDs {
|
|
rs, _ := s.read.ActivityRewardSettings.WithContext(ctx).ReadDB().Where(s.read.ActivityRewardSettings.IssueID.Eq(iid)).Order(
|
|
s.read.ActivityRewardSettings.IsBoss.Desc(),
|
|
s.read.ActivityRewardSettings.Level.Asc(),
|
|
s.read.ActivityRewardSettings.Sort.Asc(),
|
|
).Find()
|
|
for _, r := range rs {
|
|
for i := int64(0); i < r.OriginalQty; i++ {
|
|
slots = append(slots, r.ID)
|
|
}
|
|
}
|
|
}
|
|
if len(slots) > 0 {
|
|
// naive JSON encoding to compute root
|
|
// Note: avoid importing encoding/json at top, compute hash over formatted bytes
|
|
// but simpler: use []byte with little-endian concatenation
|
|
h := sha256.New()
|
|
for _, v := range slots {
|
|
var buf [8]byte
|
|
// big endian
|
|
buf[0] = byte(v >> 56)
|
|
buf[1] = byte(v >> 48)
|
|
buf[2] = byte(v >> 40)
|
|
buf[3] = byte(v >> 32)
|
|
buf[4] = byte(v >> 24)
|
|
buf[5] = byte(v >> 16)
|
|
buf[6] = byte(v >> 8)
|
|
buf[7] = byte(v)
|
|
h.Write(buf[:])
|
|
}
|
|
itemsRoot = h.Sum(nil)
|
|
}
|
|
}
|
|
ver := cur + 1
|
|
_, err = s.write.Activities.WithContext(ctx).Where(s.write.Activities.ID.Eq(act.ID)).Updates(map[string]any{
|
|
"commitment_algo": "commit-v1",
|
|
"commitment_seed_master": seed,
|
|
"commitment_seed_hash": seedHash[:],
|
|
"commitment_state_version": ver,
|
|
"commitment_items_root": itemsRoot,
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return ver, nil
|
|
}
|
|
|
|
type ActivityCommitmentSummary struct {
|
|
SeedVersion int32
|
|
Algo string
|
|
HasSeed bool
|
|
ItemsRoot []byte
|
|
}
|
|
|
|
func (s *ActivityCommitmentService) Summary(ctx context.Context, activityID int64) (ActivityCommitmentSummary, error) {
|
|
var ver int32
|
|
var algo string
|
|
var hasSeed bool
|
|
// read version
|
|
_ = s.repo.GetDbR().Raw("SELECT IFNULL(commitment_state_version,0) FROM activities WHERE id=?", activityID).Scan(&ver)
|
|
_ = s.repo.GetDbR().Raw("SELECT commitment_algo FROM activities WHERE id=?", activityID).Scan(&algo)
|
|
var seedLen *int
|
|
_ = s.repo.GetDbR().Raw("SELECT LENGTH(commitment_seed_master) FROM activities WHERE id=?", activityID).Scan(&seedLen)
|
|
hasSeed = seedLen != nil && *seedLen > 0
|
|
return ActivityCommitmentSummary{SeedVersion: ver, Algo: algo, HasSeed: hasSeed, ItemsRoot: nil}, nil
|
|
}
|