PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/forge/v1

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v1
Namespace
moul / forge
Files
18 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/forge/v1
gno
0.9

Files (18)

  • README.mdmarkdown
  • gnomod.tomltoml
  • change.gnogno
  • errors.gnogno
  • forge.gnogno
  • issue.gnogno
  • pkg.gnogno
  • ref.gnogno
  • release.gnogno
  • validate.gnogno
  • change_test.gnogno
  • forge_test.gnogno
  • helpers_test.gnogno
  • issue_test.gnogno
  • pkg_test.gnogno
  • ref_test.gnogno
  • release_test.gnogno
  • validate_test.gnogno
  • pkg.gnogno
    1package forge23import "strings"45// MaxPkgPathLen bounds a published package path. gno.land's own paths are far6// shorter; the cap exists because every stored byte is a storage deposit7// somebody pays.8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    const MaxPkgPathLen = 256
    9
    10// Package is the claim that a repo publishes a deployed package path.
    11//
    12// This is the join nothing else in the forge could express. A repo id is
    13// "<namespace>/<name>", exactly two parts, so `r/moul/home` can never be one:
    14// there was no way to ask "which repo publishes this package path", which is
    15// the first question a reader arriving from an explorer has.
    16//
    17// The claim is deliberately one-directional and unverified. A realm cannot ask
    18// the chain who deployed a path, so the forge cannot check that the repo really
    19// is the source of the package. What it can guarantee is that only someone with
    20// write access to the repo said so, and that the namespace of the repo is
    21// governed by r/sys/users. A reader weighs the claim the way they weigh a
    22// go.mod: it says where the author says the source lives.
    23type Package struct {
    24 Path string // "gno.land/r/moul/home", the deployed package path
    25 RepoID string
    26 Ref string // the ref this was published from, "" if unstated
    27 OID string // the object id, "" if unstated
    28 Tag string // the release tag, "" if not published as part of one
    29 PublishedAt int64 // block height
    30 PublishedBy address
    31}
    32
    33// ValidPkgPath reports whether s is a plausible gno package path.
    34//
    35// Deliberately shallow: it checks shape, not existence, because a realm cannot
    36// ask the chain whether a path is deployed and a check that only sometimes
    37// works is worse than one that never claims to.
    38func ValidPkgPath(s string) bool {
    39 if s == "" || len(s) > MaxPkgPathLen {
    40 return false
    41 }
    42 if strings.Contains(s, "..") || strings.HasSuffix(s, "/") {
    43 return false
    44 }
    45 parts := strings.Split(s, "/")
    46 if len(parts) < 3 {
    47 return false
    48 }
    49 for _, p := range parts {
    50 if p == "" {
    51 return false
    52 }
    53 for i := 0; i < len(p); i++ {
    54 c := p[i]
    55 ok := (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
    56 (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '_'
    57 if !ok {
    58 return false
    59 }
    60 }
    61 }
    62 return true
    63}
    64
    65// PublishPackage records that this repo publishes path, and indexes it
    66// forge-wide so PackageRepo can answer without scanning every repo.
    67//
    68// Re-publishing the same path from the same repo overwrites the entry, which is
    69// what a redeploy is. Publishing a path another repo already claims is refused:
    70// silently moving it would let any repo owner capture another project's
    71// package path in an explorer.
    72func (f *Forge) PublishPackage(actor address, height int64, repoID, path, ref, oid, tag string) (*Package, error) {
    73 r := f.Repo(repoID)
    74 if r == nil {
    75 return nil, ErrRepoNotFound
    76 }
    77 if r.Archived {
    78 return nil, ErrRepoArchived
    79 }
    80 if !r.Can(actor, RoleWriter) {
    81 return nil, ErrUnauthorized
    82 }
    83 if !ValidPkgPath(path) {
    84 return nil, ErrInvalidPkgPath
    85 }
    86 if ref != "" && !ValidRefName(ref) {
    87 return nil, ErrInvalidRefName
    88 }
    89 if oid != "" && !ValidOID(oid) {
    90 return nil, ErrInvalidOID
    91 }
    92 if tag != "" && !ValidText(tag, MaxTagLen) {
    93 return nil, ErrInvalidText
    94 }
    95 if owner := f.packages.Get(path); owner != nil {
    96 if existing := owner.(*Package); existing.RepoID != repoID {
    97 return nil, ErrPackageClaimed
    98 }
    99 }
    100
    101 p := &Package{
    102 Path: path,
    103 RepoID: repoID,
    104 Ref: ref,
    105 OID: oid,
    106 Tag: tag,
    107 PublishedAt: height,
    108 PublishedBy: actor,
    109 }
    110 f.packages.Set(path, p)
    111 r.packages.Set(path, p)
    112 return p, nil
    113}
    114
    115// UnpublishPackage withdraws the claim. The package stays on chain; only the
    116// repo's assertion about it goes away.
    117func (f *Forge) UnpublishPackage(actor address, repoID, path string) error {
    118 r := f.Repo(repoID)
    119 if r == nil {
    120 return ErrRepoNotFound
    121 }
    122 if !r.Can(actor, RoleWriter) {
    123 return ErrUnauthorized
    124 }
    125 if _, removed := r.packages.Remove(path); !removed {
    126 return ErrPackageNotFound
    127 }
    128 f.packages.Remove(path)
    129 return nil
    130}
    131
    132// PackageRepo is the lookup an explorer makes: given a deployed path, which
    133// repo claims to publish it. Returns "" when nobody does.
    134func (f *Forge) PackageRepo(path string) string {
    135 v := f.packages.Get(path)
    136 if v == nil {
    137 return ""
    138 }
    139 return v.(*Package).RepoID
    140}
    141
    142// PackageAt returns the full claim for a path, or nil.
    143func (f *Forge) PackageAt(path string) *Package {
    144 v := f.packages.Get(path)
    145 if v == nil {
    146 return nil
    147 }
    148 return v.(*Package)
    149}
    150
    151// PackageCount is how many paths the whole forge claims.
    152func (f *Forge) PackageCount() int { return f.packages.Size() }
    153
    154// IteratePackages walks every claimed path in the forge, in path order.
    155func (f *Forge) IteratePackages(offset, count int, cb func(*Package) bool) {
    156 if count <= 0 {
    157 count = f.packages.Size()
    158 }
    159 f.packages.IterateByOffset(offset, count, func(_ string, v any) bool {
    160 return cb(v.(*Package))
    161 })
    162}
    163
    164// PackageCount is how many paths this repo claims.
    165func (r *Repo) PackageCount() int { return r.packages.Size() }
    166
    167// IteratePackages walks the paths this repo claims, in path order.
    168func (r *Repo) IteratePackages(offset, count int, cb func(*Package) bool) {
    169 if count <= 0 {
    170 count = r.packages.Size()
    171 }
    172 r.packages.IterateByOffset(offset, count, func(_ string, v any) bool {
    173 return cb(v.(*Package))
    174 })
    175}
    176