Transaction

8D3F78F601D891…1E3FB800D253

Block 315,074 · index 1 · indexed

Summary

Hash
8D3F78F601D891DB2204AAB3C50E968565FE35646AC326860C311E3FB800D253
Block
315,074
Size
129119 bytes
Gas used
169,225,945 / 250,000,000
Fee
500000ugnot
Status
success

Messages

Attached funds
25000000ugnot

Arguments · 24

  1. #1memba_weighted_host
  2. #2appstore_actions.gno
  3. #3package memba_weighted_host import ( "chain" "gno.land/p/samcrew/memba_weighted_policy" "strconv" "strings" ) const AppstorePath = "gno.land/r/samcrew/memba_appstore_v3" const AppstoreMaxFee = int64(100000000) type AppstoreKind string const ( AppstoreAccept AppstoreKind = "accept-owner" AppstoreReturn AppstoreKind = "return-owner" AppstoreAbortReturn AppstoreKind = "abort-return" AppstoreAddCurator AppstoreKind = "add-curator" AppstoreRemoveCurator AppstoreKind = "remove-curator" AppstoreFee AppstoreKind = "set-fee" AppstoreTreasury AppstoreKind = "set-treasury" AppstoreUnpause AppstoreKind = "unpause" AppstoreSeal AppstoreKind = "seal-import" AppstoreApprove AppstoreKind = "approve" AppstoreReject AppstoreKind = "reject" AppstoreDelist AppstoreKind = "delist" AppstoreRestore AppstoreKind = "restore" AppstoreClearFlags AppstoreKind = "clear-flags" ) type AppstorePolicy struct{ dao, successor, treasury address } func NewAppstorePolicy(dao, successor, treasury address) AppstorePolicy { if dao != chain.PackageAddress("gno.land/r/samcrew/memba_dao") { panic("appstore policy requires the founding DAO address") } for _, a := range []address{successor, treasury} { if !a.IsValid() || a == dao || a == chain.PackageAddress(AppstorePath) { panic("appstore destination must be valid and distinct from DAO and target") } } return AppstorePolicy{dao, successor, treasury} } func (p AppstorePolicy) DAO() address { return p.dao } func (p AppstorePolicy) Successor() address { return p.successor } func (p AppstorePolicy) Treasury() address { return p.treasury } type AppstoreAction struct { kind AppstoreKind recipient address fee int64 path, reason string } func (p AppstorePolicy) Accept() AppstoreAction { return AppstoreAction{kind: AppstoreAccept} } func (p AppstorePolicy) Return() AppstoreAction { return AppstoreAction{kind: AppstoreReturn, recipient: p.successor} } func (p AppstorePolicy) AbortReturn() AppstoreAction { return AppstoreAction{kind: AppstoreAbortReturn, recipient: p.successor} } func (p AppstorePolicy) SetTreasury() AppstoreAction { return AppstoreAction{kind: AppstoreTreasury, recipient: p.treasury} } func AppstoreCurator(who address, grant bool) AppstoreAction { k := AppstoreRemoveCurator if grant { k = AppstoreAddCurator } a := AppstoreAction{kind: k, recipient: who} a.Bytes() return a } func AppstoreRegistrationFee(fee int64) AppstoreAction { a := AppstoreAction{kind: AppstoreFee, fee: fee} a.Bytes() return a } func AppstoreResume() AppstoreAction { return AppstoreAction{kind: AppstoreUnpause} } func AppstoreSealImport() AppstoreAction { return AppstoreAction{kind: AppstoreSeal} } func AppstoreModeration(kind AppstoreKind, path, reason string) AppstoreAction { a := AppstoreAction{kind: kind, path: path, reason: reason} if !a.IsListing() { panic("unsupported appstore moderation") } a.Bytes() return a } func (a AppstoreAction) Kind() AppstoreKind { return a.kind } func (a AppstoreAction) Recipient() address { return a.recipient } func (a AppstoreAction) Fee() int64 { return a.fee } func (a AppstoreAction) Path() string { return a.path } func (a AppstoreAction) Reason() string { return a.reason } func (a AppstoreAction) IsListing() bool { return a.kind == AppstoreApprove || a.kind == AppstoreReject || a.kind == AppstoreDelist || a.kind == AppstoreRestore || a.kind == AppstoreClearFlags } func (a AppstoreAction) Bytes() string { if a.IsListing() { if a.recipient != "" || a.fee != 0 || len(a.path) > 200 || !(strings.HasPrefix(a.path, "gno.land/r/") || strings.HasPrefix(a.path, "gno.land/p/")) || len(a.reason) > 500 || (a.kind != AppstoreReject && a.reason != "") { panic("invalid appstore moderation") } } else { if a.path != "" || a.reason != "" { panic("malformed appstore action") } switch a.kind { case AppstoreAccept, AppstoreUnpause, AppstoreSeal: if a.recipient != "" || a.fee != 0 { panic("malformed appstore action") } case AppstoreReturn, AppstoreAbortReturn, AppstoreTreasury, AppstoreAddCurator, AppstoreRemoveCurator: if !a.recipient.IsValid() || a.fee != 0 { panic("invalid appstore recipient") } case AppstoreFee: if a.recipient != "" || a.fee < 0 || a.fee > AppstoreMaxFee { panic("invalid appstore fee") } default: panic("unsupported appstore action") } } return "appstore/v1|" + AppstorePath + "|" + quoteJSON(string(a.kind)) + "|" + quoteJSON(string(a.recipient)) + "|" + strconv.FormatInt(a.fee, 10) + "|" + quoteJSON(a.path) + "|" + quoteJSON(a.reason) } func (a AppstoreAction) Category() memba_weighted_policy.Category { a.Bytes() if a.IsListing() { return memba_weighted_policy.Routine } switch a.kind { case AppstoreFee, AppstoreTreasury, AppstoreUnpause: return memba_weighted_policy.Financial } return memba_weighted_policy.Critical }
  4. #4appstore_host.gno
  5. #5package memba_weighted_host import "gno.land/p/samcrew/memba_weighted_policy" type AppstoreListing struct { Exists bool ContentHash, Status, Reason string Credit bool Flags int ClearKeys string } type AppstoreState struct { Owner, PendingOwner, Treasury address Fee int64 Paused, Sealed, OwnerCurator, DAOCurator, SuccessorCurator, SubjectCurator bool Listing AppstoreListing } type appstoreProposal struct { action AppstoreAction before AppstoreState } func appstoreCommitment(a AppstoreAction, s AppstoreState) string { return a.Bytes() + "|before|" + appstoreStateJSON(s) } func (h *Host) validateAppstoreAuthority(s AppstoreState) { if h.appstorePolicy == nil { panic("appstore actions are not configured") } if !s.Owner.IsValid() || !s.Treasury.IsValid() || (s.PendingOwner != "" && !s.PendingOwner.IsValid()) || s.Fee < 0 || s.Fee > AppstoreMaxFee { panic("invalid appstore pre-state") } } func (h *Host) validateAppstore(a AppstoreAction, s AppstoreState) { h.validateAppstoreAuthority(s) a.Bytes() p := h.appstorePolicy if !a.IsListing() && s.Listing != (AppstoreListing{}) { panic("unexpected appstore listing state") } if a.kind != AppstoreAddCurator && a.kind != AppstoreRemoveCurator && s.SubjectCurator { panic("unexpected appstore curator state") } if a.kind == AppstoreAccept { if s.Owner == p.DAO() || s.PendingOwner != p.DAO() { panic("DAO is not pending appstore owner") } return } if s.Owner != p.DAO() { panic("DAO is not appstore owner") } if a.kind == AppstoreAbortReturn { if a.recipient != p.Successor() || s.PendingOwner != p.Successor() { panic("appstore return does not match policy") } return } if s.PendingOwner != "" { panic("appstore authority transfer is pending") } if a.IsListing() { l := s.Listing if !s.DAOCurator { panic("DAO is not appstore curator") } if !l.Exists || l.ContentHash == "" || l.Flags < 0 { panic("invalid appstore listing") } switch a.kind { case AppstoreApprove: if l.Status != "pending" && l.Status != "rejected" { panic("appstore approval requires pending or rejected listing") } case AppstoreReject: if l.Status != "pending" { panic("appstore rejection requires pending listing") } case AppstoreDelist: if l.Status == "delisted" { panic("appstore delist is a no-op") } case AppstoreRestore: if l.Status != "delisted" { panic("appstore restore requires delisted listing") } case AppstoreClearFlags: if l.Flags <= 0 || l.ClearKeys == "" { panic("appstore has no removable flags") } } return } switch a.kind { case AppstoreReturn: if a.recipient != p.Successor() { panic("appstore successor does not match policy") } case AppstoreTreasury: if a.recipient != p.Treasury() { panic("appstore treasury does not match policy") } if s.Treasury == a.recipient { panic("appstore treasury is a no-op") } case AppstoreFee: if s.Fee == a.fee { panic("appstore fee is a no-op") } case AppstoreAddCurator: if s.SubjectCurator { panic("appstore curator grant is a no-op") } case AppstoreRemoveCurator: if !s.SubjectCurator { panic("appstore curator removal is a no-op") } case AppstoreUnpause: if !s.Paused { panic("appstore unpause is a no-op") } case AppstoreSeal: if s.Sealed { panic("appstore import already sealed") } default: panic("unsupported appstore action") } } func (h *Host) ProposeAppstore(a AppstoreAction, s AppstoreState, rlm realm) uint64 { who := assertCaller(h, rlm) h.validateAppstore(a, s) p := &roleProposal{proposer: who, appstore: &appstoreProposal{action: a, before: s}} p.ballot = h.policy.NewProposal(a.Category(), appstoreCommitment(a, s)) h.addProposal(p) return uint64(len(h.proposals)) } func (h *Host) invalidateAppstorePeers(cause string, id uint64) { members := []memba_weighted_policy.Member{} for _, seat := range h.seats { members = append(members, seat.Member) } h.policy.Reconfigure(members) h.recordInvalidation(cause, id, AppstorePath) } func (h *Host) ConsumeAppstore(id uint64, s AppstoreState, rlm realm) AppstoreAction { assertCaller(h, rlm) p := h.proposal(id) if p.appstore == nil { panic("proposal is not an appstore action") } h.validateAppstore(p.appstore.action, s) p.ballot.Consume(appstoreCommitment(p.appstore.action, s)) h.invalidateAppstorePeers(InvalidatedByExecution, id) return p.appstore.action } func (h *Host) PrepareAppstoreEmergencyPause(s AppstoreState, rlm realm) { assertCaller(h, rlm) h.validateAppstoreAuthority(s) if s.Owner != h.appstorePolicy.DAO() { panic("DAO is not appstore owner") } if s.Paused { panic("appstore is already paused") } if s.Listing != (AppstoreListing{}) || s.SubjectCurator { panic("unexpected appstore pause state") } h.invalidateAppstorePeers(InvalidatedByPause, 0) }
  6. #6appstore_reads.gno
  7. #7package memba_weighted_host import "strconv" func (h *Host) appstoreConfigJSON() string { if h.appstorePolicy == nil { return "" } return `,"appstorePolicy":{"target":` + quoteJSON(AppstorePath) + `,"successor":` + quoteJSON(string(h.appstorePolicy.Successor())) + `,"treasury":` + quoteJSON(string(h.appstorePolicy.Treasury())) + `,"maxRegistrationFee":"100000000","curatorCategory":"critical","sealCategory":"critical","moderationCategory":"routine","unpauseCategory":"financial","emergencyPause":true,"returnStagesOnly":true,"invalidatesOtherProposals":true}` } func appstoreListingJSON(s AppstoreListing) string { return `{"exists":` + strconv.FormatBool(s.Exists) + `,"contentHash":` + quoteJSON(s.ContentHash) + `,"status":` + quoteJSON(s.Status) + `,"reason":` + quoteJSON(s.Reason) + `,"credit":` + strconv.FormatBool(s.Credit) + `,"flags":` + quoteJSON(strconv.Itoa(s.Flags)) + `,"clearKeys":` + quoteJSON(s.ClearKeys) + `}` } func appstoreStateJSON(s AppstoreState) string { return `{"owner":` + quoteJSON(string(s.Owner)) + `,"pendingOwner":` + quoteJSON(string(s.PendingOwner)) + `,"treasury":` + quoteJSON(string(s.Treasury)) + `,"fee":` + quoteJSON(strconv.FormatInt(s.Fee, 10)) + `,"paused":` + strconv.FormatBool(s.Paused) + `,"sealed":` + strconv.FormatBool(s.Sealed) + `,"ownerCurator":` + strconv.FormatBool(s.OwnerCurator) + `,"daoCurator":` + strconv.FormatBool(s.DAOCurator) + `,"successorCurator":` + strconv.FormatBool(s.SuccessorCurator) + `,"subjectCurator":` + strconv.FormatBool(s.SubjectCurator) + `,"listing":` + appstoreListingJSON(s.Listing) + `}` } func appstoreActionJSON(a AppstoreAction, s AppstoreState) string { return `{"type":"appstore","target":` + quoteJSON(AppstorePath) + `,"operation":` + quoteJSON(string(a.Kind())) + `,"recipient":` + quoteJSON(string(a.Recipient())) + `,"fee":` + quoteJSON(strconv.FormatInt(a.Fee(), 10)) + `,"path":` + quoteJSON(a.Path()) + `,"reason":` + quoteJSON(a.Reason()) + `,"before":` + appstoreStateJSON(s) + `}` }
  8. #8arcade_actions.gno
  9. #9package memba_weighted_host import ( "chain" "gno.land/p/samcrew/memba_weighted_policy" ) const ArcadePath = "gno.land/r/samcrew/memba_arcade_leaderboard_v1" type ArcadeActionKind string const ( ArcadeAcceptOwner ArcadeActionKind = "accept-owner" ArcadeReturnOwner ArcadeActionKind = "return-owner" ArcadeAbortReturn ArcadeActionKind = "abort-return" ArcadeAddAttester ArcadeActionKind = "add-attester" ArcadeRemoveAttester ArcadeActionKind = "remove-attester" ArcadeUnpause ArcadeActionKind = "unpause" ) type ArcadePolicy struct{ dao, successor address } func NewArcadePolicy(dao, successor address) ArcadePolicy { if !dao.IsValid() || dao != chain.PackageAddress("gno.land/r/samcrew/memba_dao") { panic("arcade policy requires the founding DAO address") } if !successor.IsValid() || successor == dao || successor == chain.PackageAddress(ArcadePath) { panic("arcade policy requires a valid distinct successor") } return ArcadePolicy{dao: dao, successor: successor} } func (p ArcadePolicy) DAO() address { return p.dao } func (p ArcadePolicy) Successor() address { return p.successor } type ArcadeAction struct { kind ArcadeActionKind recipient address attester address } func (p ArcadePolicy) Accept() ArcadeAction { return ArcadeAction{kind: ArcadeAcceptOwner} } func (p ArcadePolicy) Return() ArcadeAction { return ArcadeAction{kind: ArcadeReturnOwner, recipient: p.successor} } func (p ArcadePolicy) AbortReturn() ArcadeAction { return ArcadeAction{kind: ArcadeAbortReturn, recipient: p.successor} } func (p ArcadePolicy) AddAttester(who address) ArcadeAction { if who == p.DAO() || who == p.Successor() || who == chain.PackageAddress(ArcadePath) { panic("arcade attester must be a dedicated external account") } a := ArcadeAction{kind: ArcadeAddAttester, attester: who} a.Bytes() return a } func ArcadeRevokeAttester(who address) ArcadeAction { a := ArcadeAction{kind: ArcadeRemoveAttester, attester: who} a.Bytes() return a } func ArcadeResume() ArcadeAction { return ArcadeAction{kind: ArcadeUnpause} } func (a ArcadeAction) Kind() ArcadeActionKind { return a.kind } func (a ArcadeAction) Recipient() address { return a.recipient } func (a ArcadeAction) Attester() address { return a.attester } func (a ArcadeAction) Bytes() string { base := "arcade/v1|" + ArcadePath + "|" + string(a.kind) switch a.kind { case ArcadeAcceptOwner, ArcadeUnpause: if a.recipient != "" || a.attester != "" { panic("malformed arcade action") } return base case ArcadeReturnOwner, ArcadeAbortReturn: if !a.recipient.IsValid() || a.attester != "" { panic("malformed arcade return action") } return base + "|" + string(a.recipient) case ArcadeAddAttester, ArcadeRemoveAttester: if !a.attester.IsValid() || a.recipient != "" { panic("invalid arcade attester action") } return base + "|" + string(a.attester) default: panic("unsupported arcade action") } } func (a ArcadeAction) Category() memba_weighted_policy.Category { a.Bytes() if a.kind == ArcadeUnpause { return memba_weighted_policy.Financial } return memba_weighted_policy.Critical }
  10. #10arcade_host.gno
  11. #11package memba_weighted_host import "gno.land/p/samcrew/memba_weighted_policy" // Explicit allowlist bits are separate from the owner's implicit attester right. type ArcadeState struct { Owner, PendingOwner address Paused, OwnerListed, DAOListed, SuccessorListed, SubjectListed bool } type arcadeProposal struct { action ArcadeAction before ArcadeState } func arcadeCommitment(a ArcadeAction, s ArcadeState) string { return a.Bytes() + "|before|" + arcadeStateJSON(s) } func (h *Host) validateArcadeAuthority(s ArcadeState) { if h.arcadePolicy == nil { panic("arcade actions are not configured") } if !s.Owner.IsValid() || (s.PendingOwner != "" && !s.PendingOwner.IsValid()) { panic("invalid arcade authority pre-state") } } func (h *Host) validateArcade(a ArcadeAction, s ArcadeState) { h.validateArcadeAuthority(s) a.Bytes() p := h.arcadePolicy if a.Attester() == "" && s.SubjectListed { panic("unexpected arcade subject pre-state") } if a.Kind() == ArcadeAcceptOwner { if s.Owner == p.DAO() || s.PendingOwner != p.DAO() { panic("DAO is not pending arcade owner") } return } if s.Owner != p.DAO() { panic("DAO is not arcade owner") } if a.Kind() == ArcadeAbortReturn { if a.Recipient() != p.Successor() || s.PendingOwner != p.Successor() { panic("arcade return does not match policy") } return } if s.PendingOwner != "" { panic("arcade authority transfer is pending") } switch a.Kind() { case ArcadeReturnOwner: if a.Recipient() != p.Successor() { panic("arcade successor does not match policy") } case ArcadeAddAttester: p.AddAttester(a.Attester()) if s.SubjectListed { panic("arcade attester grant is a no-op") } case ArcadeRemoveAttester: if !s.SubjectListed { panic("arcade attester removal is a no-op") } case ArcadeUnpause: if !s.Paused { panic("arcade unpause is a no-op") } default: panic("unsupported arcade action") } } func (h *Host) ProposeArcade(a ArcadeAction, s ArcadeState, rlm realm) uint64 { who := assertCaller(h, rlm) h.validateArcade(a, s) p := &roleProposal{proposer: who, arcade: &arcadeProposal{action: a, before: s}} p.ballot = h.policy.NewProposal(a.Category(), arcadeCommitment(a, s)) h.addProposal(p) return uint64(len(h.proposals)) } func (h *Host) invalidateArcadePeers(cause string, id uint64) { members := []memba_weighted_policy.Member{} for _, seat := range h.seats { members = append(members, seat.Member) } h.policy.Reconfigure(members) h.recordInvalidation(cause, id, ArcadePath) } func (h *Host) ConsumeArcade(id uint64, s ArcadeState, rlm realm) ArcadeAction { assertCaller(h, rlm) p := h.proposal(id) if p.arcade == nil { panic("proposal is not an arcade action") } h.validateArcade(p.arcade.action, s) p.ballot.Consume(arcadeCommitment(p.arcade.action, s)) h.invalidateArcadePeers(InvalidatedByExecution, id) return p.arcade.action } // A fixed pause-only capability, not a proposal category or a bool-controlled // escape. An already-paused call cannot invalidate pending unpause approvals. func (h *Host) PrepareArcadeEmergencyPause(s ArcadeState, rlm realm) { assertCaller(h, rlm) h.validateArcadeAuthority(s) if s.Owner != h.arcadePolicy.DAO() { panic("DAO is not arcade owner") } if s.Paused { panic("arcade is already paused") } if s.SubjectListed { panic("unexpected arcade subject pre-state") } h.invalidateArcadePeers(InvalidatedByPause, 0) }
  12. #12arcade_reads.gno
  13. #13package memba_weighted_host import "strconv" func (h *Host) arcadeConfigJSON() string { if h.arcadePolicy == nil { return "" } return `,"arcadePolicy":{"target":` + quoteJSON(ArcadePath) + `,"successor":` + quoteJSON(string(h.arcadePolicy.Successor())) + `,"attesterCategory":"critical","unpauseCategory":"financial","emergencyPause":true,"returnStagesOnly":true,"invalidatesOtherProposals":true}` } func arcadeStateJSON(s ArcadeState) string { return `{"owner":` + quoteJSON(string(s.Owner)) + `,"pendingOwner":` + quoteJSON(string(s.PendingOwner)) + `,"paused":` + strconv.FormatBool(s.Paused) + `,"ownerListed":` + strconv.FormatBool(s.OwnerListed) + `,"daoListed":` + strconv.FormatBool(s.DAOListed) + `,"successorListed":` + strconv.FormatBool(s.SuccessorListed) + `,"subjectListed":` + strconv.FormatBool(s.SubjectListed) + `}` } func arcadeActionJSON(a ArcadeAction, s ArcadeState) string { return `{"type":"arcade","target":` + quoteJSON(ArcadePath) + `,"operation":` + quoteJSON(string(a.Kind())) + `,"recipient":` + quoteJSON(string(a.Recipient())) + `,"attester":` + quoteJSON(string(a.Attester())) + `,"before":` + arcadeStateJSON(s) + `}` }
  14. #14badges_actions.gno
  15. #15package memba_weighted_host import ( "chain" "gno.land/p/samcrew/memba_weighted_policy" ) const BadgesPath = "gno.land/r/samcrew/gnobuilders_badges_v2" type BadgesActionKind string const ( BadgesAcceptOwner BadgesActionKind = "accept-owner" BadgesReturnOwner BadgesActionKind = "return-owner" BadgesAbortReturn BadgesActionKind = "abort-return" BadgesAddAdmin BadgesActionKind = "add-admin" BadgesRemoveAdmin BadgesActionKind = "remove-admin" BadgesUnpause BadgesActionKind = "unpause" ) type BadgesPolicy struct{ dao, successor address } func NewBadgesPolicy(dao, successor address) BadgesPolicy { if !dao.IsValid() || dao != chain.PackageAddress("gno.land/r/samcrew/memba_dao") { panic("badges policy requires the founding DAO address") } if !successor.IsValid() || successor == dao || successor == chain.PackageAddress(BadgesPath) { panic("badges policy requires a valid distinct successor") } return BadgesPolicy{dao: dao, successor: successor} } func (p BadgesPolicy) DAO() address { return p.dao } func (p BadgesPolicy) Successor() address { return p.successor } type BadgesAction struct { kind BadgesActionKind recipient address subject address } func (p BadgesPolicy) Accept() BadgesAction { return BadgesAction{kind: BadgesAcceptOwner} } func (p BadgesPolicy) Return() BadgesAction { return BadgesAction{kind: BadgesReturnOwner, recipient: p.successor} } func (p BadgesPolicy) AbortReturn() BadgesAction { return BadgesAction{kind: BadgesAbortReturn, recipient: p.successor} } func (p BadgesPolicy) AddAdmin(who address) BadgesAction { if who == p.dao || who == chain.PackageAddress(BadgesPath) { panic("badges admin must be an external account") } a := BadgesAction{kind: BadgesAddAdmin, subject: who} a.Bytes() return a } func BadgesRevokeAdmin(who address) BadgesAction { a := BadgesAction{kind: BadgesRemoveAdmin, subject: who} a.Bytes() return a } func BadgesResume() BadgesAction { return BadgesAction{kind: BadgesUnpause} } func (a BadgesAction) Kind() BadgesActionKind { return a.kind } func (a BadgesAction) Recipient() address { return a.recipient } func (a BadgesAction) Subject() address { return a.subject } func (a BadgesAction) Bytes() string { base := "badges/v1|" + BadgesPath + "|" + string(a.kind) switch a.kind { case BadgesAcceptOwner, BadgesUnpause: if a.recipient != "" || a.subject != "" { panic("malformed badges action") } return base case BadgesReturnOwner, BadgesAbortReturn: if !a.recipient.IsValid() || a.subject != "" { panic("malformed badges return action") } return base + "|" + string(a.recipient) case BadgesAddAdmin, BadgesRemoveAdmin: if !a.subject.IsValid() || a.recipient != "" { panic("invalid badges admin action") } return base + "|" + string(a.subject) default: panic("unsupported badges action") } } func (a BadgesAction) Category() memba_weighted_policy.Category { a.Bytes() if a.kind == BadgesUnpause { return memba_weighted_policy.Financial } return memba_weighted_policy.Critical }
  16. #16badges_host.gno
  17. #17package memba_weighted_host import "gno.land/p/samcrew/memba_weighted_policy" const BadgesMaxAdmins = 10 // Admin bits are explicit grants. The current owner's authority to appoint is // separate from those bits, but target ownership always installs an admin grant. type BadgesState struct { Owner, PendingOwner address Paused bool AdminCount int OwnerAdmin, DAOAdmin, SuccessorAdmin, SubjectAdmin bool } type badgesProposal struct { action BadgesAction before BadgesState } func badgesCommitment(a BadgesAction, s BadgesState) string { return a.Bytes() + "|before|" + badgesStateJSON(s) } func (h *Host) validateBadgesAuthority(s BadgesState) { if h.badgesPolicy == nil { panic("badges actions are not configured") } if !s.Owner.IsValid() || (s.PendingOwner != "" && !s.PendingOwner.IsValid()) || !s.OwnerAdmin || s.AdminCount < 1 || s.AdminCount > BadgesMaxAdmins { panic("invalid badges authority pre-state") } } func (h *Host) validateBadges(a BadgesAction, s BadgesState) { h.validateBadgesAuthority(s) a.Bytes() p := h.badgesPolicy if a.Subject() == "" && s.SubjectAdmin { panic("unexpected badges subject pre-state") } if a.Kind() == BadgesAcceptOwner { if s.Owner == p.DAO() || s.PendingOwner != p.DAO() { panic("DAO is not pending badges owner") } return } if s.Owner != p.DAO() || !s.DAOAdmin { panic("DAO is not badges owner/admin") } if a.Kind() == BadgesAbortReturn { if a.Recipient() != p.Successor() || s.PendingOwner != p.Successor() { panic("badges return does not match policy") } return } if s.PendingOwner != "" { panic("badges authority transfer is pending") } switch a.Kind() { case BadgesReturnOwner: if a.Recipient() != p.Successor() { panic("badges successor does not match policy") } case BadgesAddAdmin: p.AddAdmin(a.Subject()) if s.SubjectAdmin { panic("badges admin grant is a no-op") } if s.AdminCount >= BadgesMaxAdmins { panic("badges admin limit reached") } case BadgesRemoveAdmin: if a.Subject() == s.Owner { panic("cannot remove badges owner admin") } if !s.SubjectAdmin { panic("badges admin removal is a no-op") } case BadgesUnpause: if !s.Paused { panic("badges unpause is a no-op") } default: panic("unsupported badges action") } } func (h *Host) ProposeBadges(a BadgesAction, s BadgesState, rlm realm) uint64 { who := assertCaller(h, rlm) h.validateBadges(a, s) p := &roleProposal{proposer: who, badges: &badgesProposal{action: a, before: s}} p.ballot = h.policy.NewProposal(a.Category(), badgesCommitment(a, s)) h.addProposal(p) return uint64(len(h.proposals)) } func (h *Host) invalidateBadgesPeers(cause string, id uint64) { members := []memba_weighted_policy.Member{} for _, seat := range h.seats { members = append(members, seat.Member) } h.policy.Reconfigure(members) h.recordInvalidation(cause, id, BadgesPath) } func (h *Host) ConsumeBadges(id uint64, s BadgesState, rlm realm) BadgesAction { assertCaller(h, rlm) p := h.proposal(id) if p.badges == nil { panic("proposal is not a badges action") } h.validateBadges(p.badges.action, s) p.ballot.Consume(badgesCommitment(p.badges.action, s)) h.invalidateBadgesPeers(InvalidatedByExecution, id) return p.badges.action } func (h *Host) PrepareBadgesEmergencyPause(s BadgesState, rlm realm) { assertCaller(h, rlm) h.validateBadgesAuthority(s) if s.Owner != h.badgesPolicy.DAO() || !s.DAOAdmin { panic("DAO is not badges owner/admin") } if s.Paused { panic("badges is already paused") } if s.SubjectAdmin { panic("unexpected badges subject pre-state") } h.invalidateBadgesPeers(InvalidatedByPause, 0) }
  18. #18badges_reads.gno
  19. #19package memba_weighted_host import "strconv" func (h *Host) badgesConfigJSON() string { if h.badgesPolicy == nil { return "" } return `,"badgesPolicy":{"target":` + quoteJSON(BadgesPath) + `,"successor":` + quoteJSON(string(h.badgesPolicy.Successor())) + `,"adminCategory":"critical","unpauseCategory":"financial","emergencyPause":true,"returnStagesOnly":true,"invalidatesOtherProposals":true}` } func badgesStateJSON(s BadgesState) string { return `{"owner":` + quoteJSON(string(s.Owner)) + `,"pendingOwner":` + quoteJSON(string(s.PendingOwner)) + `,"paused":` + strconv.FormatBool(s.Paused) + `,"adminCount":` + quoteJSON(strconv.Itoa(s.AdminCount)) + `,"ownerAdmin":` + strconv.FormatBool(s.OwnerAdmin) + `,"daoAdmin":` + strconv.FormatBool(s.DAOAdmin) + `,"successorAdmin":` + strconv.FormatBool(s.SuccessorAdmin) + `,"subjectAdmin":` + strconv.FormatBool(s.SubjectAdmin) + `}` } func badgesActionJSON(a BadgesAction, s BadgesState) string { return `{"type":"badges","target":` + quoteJSON(BadgesPath) + `,"operation":` + quoteJSON(string(a.Kind())) + `,"recipient":` + quoteJSON(string(a.Recipient())) + `,"subject":` + quoteJSON(string(a.Subject())) + `,"before":` + badgesStateJSON(s) + `}` }
  20. #20channels_actions.gno
  21. #21package memba_weighted_host import ( "chain" "strconv" "strings" "gno.land/p/samcrew/memba_weighted_policy" ) const ChannelsPath = "gno.land/r/samcrew/memba_dao_channels_v2" type ChannelsActionKind string const ( ChannelsAcceptOwner ChannelsActionKind = "accept-owner" ChannelsReturnOwner ChannelsActionKind = "return-owner" ChannelsAbortReturn ChannelsActionKind = "abort-return" ChannelsAddMember ChannelsActionKind = "add-member" ChannelsRemoveMember ChannelsActionKind = "remove-member" ChannelsSetRoles ChannelsActionKind = "set-roles" ChannelsCreateText ChannelsActionKind = "create-text-channel" ChannelsUnpause ChannelsActionKind = "unpause" ) type ChannelsPolicy struct{ dao, successor address } func NewChannelsPolicy(dao, successor address) ChannelsPolicy { if !dao.IsValid() || dao != chain.PackageAddress("gno.land/r/samcrew/memba_dao") { panic("channels policy requires the founding DAO address") } if !successor.IsValid() || successor == dao || successor == chain.PackageAddress(ChannelsPath) { panic("channels policy requires a valid distinct successor") } return ChannelsPolicy{dao: dao, successor: successor} } func (p ChannelsPolicy) DAO() address { return p.dao } func (p ChannelsPolicy) Successor() address { return p.successor } type ChannelsAction struct { kind ChannelsActionKind recipient, subject address roles string name, description string } func (p ChannelsPolicy) Accept() ChannelsAction { return ChannelsAction{kind: ChannelsAcceptOwner} } func (p ChannelsPolicy) Return() ChannelsAction { return ChannelsAction{kind: ChannelsReturnOwner, recipient: p.successor} } func (p ChannelsPolicy) AbortReturn() ChannelsAction { return ChannelsAction{kind: ChannelsAbortReturn, recipient: p.successor} } func (p ChannelsPolicy) AddMember(who address, roles string) ChannelsAction { a:=ChannelsAction{kind: ChannelsAddMember, subject:who, roles:roles}; a.Bytes(); return a } func (p ChannelsPolicy) RemoveMember(who address) ChannelsAction { a:=ChannelsAction{kind: ChannelsRemoveMember, subject:who}; a.Bytes(); return a } func (p ChannelsPolicy) SetRoles(who address, roles string) ChannelsAction { a:=ChannelsAction{kind: ChannelsSetRoles, subject:who, roles:roles}; a.Bytes(); return a } func (p ChannelsPolicy) CreateText(name, description string) ChannelsAction { a:=ChannelsAction{kind: ChannelsCreateText, name:name, description:description}; a.Bytes(); return a } func ChannelsResume() ChannelsAction { return ChannelsAction{kind: ChannelsUnpause} } func (a ChannelsAction) Kind() ChannelsActionKind { return a.kind } func (a ChannelsAction) Recipient() address { return a.recipient } func (a ChannelsAction) Subject() address { return a.subject } func (a ChannelsAction) Roles() string { return a.roles } func (a ChannelsAction) Name() string { return a.name } func (a ChannelsAction) Description() string { return a.description } func validChannelsName(name string) bool { if len(name)<1 || len(name)>50 { return false } for _,c:=range name { if !((c>='a'&&c<='z')||(c>='0'&&c<='9')||c=='-') { return false } } return true } func validChannelsDescription(description string) bool { if len(description)<1 || len(description)>200 { return false } for _,c:=range description { if c<32 || c==127 { return false } } return true } // Only the four target ACL roles are representable; every role list has a // canonical order, so a voted commitment cannot alias another role grant. func canonicalChannelsRoles(roles string) bool { if roles == "" { return false } parts:=strings.Split(roles, ",") if len(parts)>4 { return false } previous:=-1 for _,part:=range parts { index:=-1 switch part { case "admin": index=0; case "dev": index=1; case "ops": index=2; case "member": index=3 } if index<=previous { return false } previous=index } return true } func (a ChannelsAction) Bytes() string { base:="channels/v1|"+ChannelsPath+"|"+string(a.kind) switch a.kind { case ChannelsAcceptOwner, ChannelsUnpause: if a.recipient!="" || a.subject!="" || a.roles!="" || a.name!="" || a.description!="" { panic("malformed channels action") } return base case ChannelsReturnOwner, ChannelsAbortReturn: if !a.recipient.IsValid() || a.subject!="" || a.roles!="" || a.name!="" || a.description!="" { panic("malformed channels return action") } return base+"|"+string(a.recipient) case ChannelsAddMember, ChannelsSetRoles: if !a.subject.IsValid() || a.recipient!="" || a.name!="" || a.description!="" || !canonicalChannelsRoles(a.roles) { panic("invalid channels member action") } return base+"|"+string(a.subject)+"|"+strconv.Itoa(len(a.roles))+":"+a.roles case ChannelsRemoveMember: if !a.subject.IsValid() || a.recipient!="" || a.roles!="" || a.name!="" || a.description!="" { panic("invalid channels member action") } return base+"|"+string(a.subject) case ChannelsCreateText: if a.recipient!="" || a.subject!="" || a.roles!="" || !validChannelsName(a.name) || !validChannelsDescription(a.description) { panic("invalid channels text creation") } return base+"|"+a.name+"|"+strconv.Itoa(len(a.description))+":"+a.description default: panic("unsupported channels action") } } func (a ChannelsAction) Category() memba_weighted_policy.Category { a.Bytes() if a.kind==ChannelsUnpause { return memba_weighted_policy.Financial } return memba_weighted_policy.Critical }
  22. #22channels_host.gno
  23. #23package memba_weighted_host import ( "chain" "gno.land/p/samcrew/memba_weighted_policy" ) type ChannelsState struct { Owner, PendingOwner address Paused bool MemberCount int MembershipRevision uint64 OwnerMember, DAOMember, SuccessorMember, SubjectMember bool OwnerRoles, DAORoles, SuccessorRoles, SubjectRoles string ChannelCount int ChannelExists bool ChannelDescription, ChannelType, ChannelReadRoles, ChannelWriteRoles string } type channelsProposal struct { action ChannelsAction; before ChannelsState } func channelsCommitment(a ChannelsAction, s ChannelsState) string { return a.Bytes()+"|before|"+channelsStateJSON(s) } func (h *Host) validateChannels(a ChannelsAction, s ChannelsState) { if h.channelsPolicy==nil { panic("channels actions are not configured") } a.Bytes() p:=h.channelsPolicy if !s.Owner.IsValid() || (s.PendingOwner!="" && !s.PendingOwner.IsValid()) || s.MemberCount<1 || !s.OwnerMember || s.ChannelCount<0 || s.ChannelCount>20 { panic("invalid channels authority pre-state") } if a.Subject()=="" && (s.SubjectMember || s.SubjectRoles!="") { panic("unexpected channels subject pre-state") } if a.Name()=="" && (s.ChannelExists || s.ChannelDescription!="" || s.ChannelType!="" || s.ChannelReadRoles!="" || s.ChannelWriteRoles!="") { panic("unexpected channels creation pre-state") } if a.Kind()==ChannelsAcceptOwner { if s.Owner==p.DAO() || s.PendingOwner!=p.DAO() || s.DAOMember { panic("DAO is not pending channels owner or already has membership") } return } if s.Owner!=p.DAO() || !s.DAOMember || s.DAORoles!=s.OwnerRoles { panic("DAO is not channels owner") } if a.Kind()==ChannelsAbortReturn { if a.Recipient()!=p.Successor() || s.PendingOwner!=p.Successor() { panic("channels return does not match policy") } return } if s.PendingOwner!="" { panic("channels authority transfer is pending") } switch a.Kind() { case ChannelsReturnOwner: if a.Recipient()!=p.Successor() || s.SuccessorMember { panic("channels successor mismatch or preexisting membership") } case ChannelsAddMember, ChannelsRemoveMember, ChannelsSetRoles: if a.Subject()==p.DAO() || a.Subject()==chain.PackageAddress(ChannelsPath) || (a.Subject()==p.Successor() && a.Kind()!=ChannelsRemoveMember) { panic("channels member action targets protected authority") } if a.Kind()==ChannelsAddMember && s.SubjectMember { panic("channels member already exists") } if a.Kind()!=ChannelsAddMember && !s.SubjectMember { panic("channels member does not exist") } if a.Kind()==ChannelsSetRoles && s.SubjectRoles==a.Roles() { panic("channels roles are unchanged") } case ChannelsCreateText: if s.ChannelCount>=20 || s.ChannelExists { panic("channels text creation unavailable") } case ChannelsUnpause: if !s.Paused { panic("channels unpause is a no-op") } default: panic("unsupported channels action") } } func (h *Host) ProposeChannels(a ChannelsAction, s ChannelsState, rlm realm) uint64 { who:=assertCaller(h,rlm) h.validateChannels(a,s) p:=&roleProposal{proposer:who,channels:&channelsProposal{action:a,before:s}} p.ballot=h.policy.NewProposal(a.Category(),channelsCommitment(a,s)) h.addProposal(p) return uint64(len(h.proposals)) } func (h *Host) invalidateChannelsPeers(cause string, id uint64) { members:=[]memba_weighted_policy.Member{} for _,seat:=range h.seats { members=append(members,seat.Member) } h.policy.Reconfigure(members) h.recordInvalidation(cause, id, ChannelsPath) } func (h *Host) ConsumeChannels(id uint64, s ChannelsState, rlm realm) ChannelsAction { assertCaller(h,rlm) p:=h.proposal(id) if p.channels==nil { panic("proposal is not a channels action") } h.validateChannels(p.channels.action,s) p.ballot.Consume(channelsCommitment(p.channels.action,s)) h.invalidateChannelsPeers(InvalidatedByExecution, id) return p.channels.action } func (h *Host) PrepareChannelsEmergencyPause(s ChannelsState, rlm realm) { assertCaller(h,rlm) if h.channelsPolicy==nil || s.Owner!=h.channelsPolicy.DAO() || s.Paused || !s.OwnerMember || !s.DAOMember || s.DAORoles!=s.OwnerRoles { panic("channels emergency pause unavailable") } h.invalidateChannelsPeers(InvalidatedByPause, 0) }
  24. #24channels_reads.gno

Result log

msg:0,success:true,log:,events:[]

← Back to block 315,074