protowire.mx raw

   1  // Package protowire binds the musiquay protocol vocabulary to the relay's
   2  // native event type. The vocabulary is a leaf (protocol imports only runtime,
   3  // errorf and text), so this package may import it without dragging the relay
   4  // into the vocabulary: it imports nothing from morly.
   5  //
   6  // Two jobs live here. The encoding job turns an event's tag list into the
   7  // [][]string the protocol parsers take. The policy job answers what a
   8  // blob-bearing event's asset address is, which the vocabulary itself cannot
   9  // know: it depends on the event kind.
  10  package protowire
  11  
  12  import (
  13  	"git.smesh.lol/musiquay/pkg/protocol"
  14  	"git.smesh.lol/nostr/pkg/event"
  15  )
  16  
  17  // TagsOf copies an event's tag list into the [][]string shape the protocol
  18  // parsers take.
  19  //
  20  // The outer slice and every inner row are freshly allocated and presized:
  21  // ev.Tags.T already knows both lengths, so there is nothing to grow and no
  22  // push. A nil tag entry is left as a nil row rather than copied, since a
  23  // parser skips rows shorter than one element anyway.
  24  func TagsOf(ev *event.E) (tags [][]string) {
  25  	if ev == nil || ev.Tags == nil {
  26  		return nil
  27  	}
  28  	src := ev.Tags.T
  29  	tags = [][]string{:len(src)}
  30  	for i := range src {
  31  		tg := src[i]
  32  		if tg == nil {
  33  			continue
  34  		}
  35  		row := []string{:len(tg.T)}
  36  		for j := range tg.T {
  37  			row[j] = tg.T[j]
  38  		}
  39  		tags[i] = row
  40  	}
  41  	return
  42  }
  43  
  44  // ValidateEvent parses and validates a payload by kind.
  45  //
  46  // Every kind the protocol defines and has a parser for is checked. Kinds the
  47  // protocol defines without a parser, and every kind outside the vocabulary,
  48  // return nil: the relay stores a payload this build has no rule for rather
  49  // than rejecting it. A Musiquay kind added before its parser must therefore
  50  // fall through here, never reach a nil parser.
  51  func ValidateEvent(ev *event.E) (err error) {
  52  	if ev == nil {
  53  		return nil
  54  	}
  55  	tags := TagsOf(ev)
  56  	content := string(ev.Content)
  57  	switch ev.Kind {
  58  	case protocol.KindTrack:
  59  		v, perr := protocol.ParseTrack(tags, content)
  60  		if perr != nil {
  61  			return perr
  62  		}
  63  		return v.Validate()
  64  	case protocol.KindAlbum:
  65  		v, perr := protocol.ParseAlbum(tags, content)
  66  		if perr != nil {
  67  			return perr
  68  		}
  69  		return v.Validate()
  70  	case protocol.KindPlaylist:
  71  		v, perr := protocol.ParsePlaylist(tags, content)
  72  		if perr != nil {
  73  			return perr
  74  		}
  75  		return v.Validate()
  76  	case protocol.KindSetlist:
  77  		v, perr := protocol.ParseSetlist(tags, content)
  78  		if perr != nil {
  79  			return perr
  80  		}
  81  		return v.Validate()
  82  	case protocol.KindReleaseCredits:
  83  		v, perr := protocol.ParseReleaseCredits(tags, content)
  84  		if perr != nil {
  85  			return perr
  86  		}
  87  		return v.Validate()
  88  	case protocol.KindTrackCredits:
  89  		v, perr := protocol.ParseTrackCredits(tags, content)
  90  		if perr != nil {
  91  			return perr
  92  		}
  93  		return v.Validate()
  94  	case protocol.KindEdition:
  95  		v, perr := protocol.ParseEdition(tags, content)
  96  		if perr != nil {
  97  			return perr
  98  		}
  99  		return v.Validate()
 100  	case protocol.KindAccessFilter:
 101  		v, perr := protocol.ParseAccessFilter(tags, content)
 102  		if perr != nil {
 103  			return perr
 104  		}
 105  		return v.Validate()
 106  	case protocol.KindPlayReport:
 107  		v, perr := protocol.ParsePlayReport(tags, content)
 108  		if perr != nil {
 109  			return perr
 110  		}
 111  		return v.Validate()
 112  	case protocol.KindPurchaseOrder:
 113  		v, perr := protocol.ParsePurchaseOrder(tags, content)
 114  		if perr != nil {
 115  			return perr
 116  		}
 117  		return v.Validate()
 118  	case protocol.KindDeliveryReceipt:
 119  		v, perr := protocol.ParseDeliveryReceipt(tags, content)
 120  		if perr != nil {
 121  			return perr
 122  		}
 123  		return v.Validate()
 124  	case protocol.KindHostAggregate:
 125  		v, perr := protocol.ParseHostAggregate(tags, content)
 126  		if perr != nil {
 127  			return perr
 128  		}
 129  		return v.Validate()
 130  	case protocol.KindPublisherRollup:
 131  		v, perr := protocol.ParsePublisherRollup(tags, content)
 132  		if perr != nil {
 133  			return perr
 134  		}
 135  		return v.Validate()
 136  	case protocol.KindCreditLabel:
 137  		v, perr := protocol.ParseCreditLabel(tags, content)
 138  		if perr != nil {
 139  			return perr
 140  		}
 141  		return v.Validate()
 142  	case protocol.KindComment:
 143  		v, perr := protocol.ParseContribution(tags, content)
 144  		if perr != nil {
 145  			return perr
 146  		}
 147  		return v.Validate()
 148  	case protocol.KindCalendar:
 149  		v, perr := protocol.ParseCalendarEvent(tags, content)
 150  		if perr != nil {
 151  			return perr
 152  		}
 153  		return v.Validate()
 154  	case protocol.KindRSVP:
 155  		v, perr := protocol.ParseRSVP(tags, content)
 156  		if perr != nil {
 157  			return perr
 158  		}
 159  		return v.Validate()
 160  	// The NIP-15 payloads are JSON in content with no payload tags, so their
 161  	// parsers take content alone.
 162  	case protocol.KindStall:
 163  		v, perr := protocol.ParseStall(content)
 164  		if perr != nil {
 165  			return perr
 166  		}
 167  		return v.Validate()
 168  	case protocol.KindProduct:
 169  		v, perr := protocol.ParseProduct(content)
 170  		if perr != nil {
 171  			return perr
 172  		}
 173  		return v.Validate()
 174  	case protocol.KindListing:
 175  		v, perr := protocol.ParseListing(tags, content)
 176  		if perr != nil {
 177  			return perr
 178  		}
 179  		return v.Validate()
 180  	}
 181  	return nil
 182  }
 183  
 184  // AssetOf returns the asset address a blob-bearing event belongs to: the first
 185  // a tag for a track and a delivery receipt, and the asset inside the d value
 186  // for an access filter. Anything else has no asset here.
 187  //
 188  // An access filter whose d is a subscription tier (sub:<retailer>:<channel>)
 189  // names no asset, and so does one whose d fails to parse; both return ok=false
 190  // rather than an empty string that a caller might index.
 191  func AssetOf(ev *event.E) (asset string, ok bool) {
 192  	if ev == nil {
 193  		return "", false
 194  	}
 195  	switch ev.Kind {
 196  	case protocol.KindTrack, protocol.KindDeliveryReceipt:
 197  		tv := ev.Tags.GetFirst([]byte("a"))
 198  		if tv == nil || tv.Len() < 2 {
 199  			return "", false
 200  		}
 201  		return string(tv.Value()), true
 202  	case protocol.KindAccessFilter:
 203  		dv := ev.Tags.GetFirst([]byte("d"))
 204  		if dv == nil || dv.Len() < 2 {
 205  			return "", false
 206  		}
 207  		tier, perr := protocol.ParseFilterD(string(dv.Value()))
 208  		if perr != nil || tier.Asset == nil {
 209  			return "", false
 210  		}
 211  		return tier.Asset.String(), true
 212  	}
 213  	return "", false
 214  }
 215