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/x/social/vouch/v0

Package
Open in gnoweb ↗

Overview

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

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • vouch.gnogno
  • vouch_test.gnogno
vouch_test.gnogno
1package vouch23import (4	"strings"5	"testing"67	"gno.land/p/nt/testutils/v0"8	"gno.land/p/nt/uassert/v0"9	"gno.land/p/nt/urequire/v0"10)1112var (13	alice = testutils.TestAddress("alice")14	bob   = testutils.TestAddress("bob")15	carol = testutils.TestAddress("carol")16	dave  = testutils.TestAddress("dave")17)1819const realmPath = "gno.land/r/moul/x/social/vouch/v0"2021func TestValidReason(t *testing.T) {22	tests := []struct {23		name   string24		reason string25		want   bool26	}{27		{"plain", "worked with them for a year", true},28		{"markdown is allowed and escaped later", "[x](y) | z", true},29		{"at the limit", strings.Repeat("x", MaxReasonLen), true},30		{"empty", "", false},31		{"blank", "   ", false},32		{"too long", strings.Repeat("x", MaxReasonLen+1), false},33		{"a reason is one line", "met them\nat a conference", false},34		{"no tabs either", "met them\there", false},35		{"control character", "a\x01b", false},36		{"delete character", "a\x7fb", false},37	}38	for _, tt := range tests {39		uassert.Equal(t, tt.want, ValidReason(tt.reason), tt.name)40	}41}4243func TestRecordRefusesWhatItCannotStore(t *testing.T) {44	g := NewGraph()4546	_, err := g.Record(alice, alice, "myself", 0, 100)47	uassert.ErrorIs(t, err, ErrSelfVouch)4849	_, err = g.Record(alice, bob, "", 0, 100)50	uassert.ErrorIs(t, err, ErrBadReason)5152	_, err = g.Record(alice, bob, "fine", -1, 100)53	uassert.ErrorIs(t, err, ErrBadBond)5455	uassert.Equal(t, 0, g.Count())56	uassert.Equal(t, 0, g.People())57	uassert.Equal(t, 0, g.ScoreOf(alice))58}5960// A score counts people. This is the property every caller of IsTrusted is61// relying on, so it is the one worth pinning hardest.62func TestScoreCountsPeopleAndNotVouches(t *testing.T) {63	g := NewGraph()6465	updated, err := g.Record(bob, alice, "known them for years", 0, 100)66	urequire.NoError(t, err)67	uassert.False(t, updated, "the first vouch is not an update")68	uassert.Equal(t, 1, g.ScoreOf(alice))6970	// Saying it again is an edit, not a second voice.71	updated, err = g.Record(bob, alice, "still true", 0, 101)72	urequire.NoError(t, err)73	uassert.True(t, updated, "the second vouch replaced the first")74	uassert.Equal(t, 1, g.ScoreOf(alice), "one person is one point, however often they say it")75	uassert.Equal(t, "still true", g.ReasonFrom(bob, alice), "the reason was updated")76	uassert.Equal(t, 1, g.Count())7778	// A different person does move it.79	_, err = g.Record(carol, alice, "we shipped together", 0, 102)80	urequire.NoError(t, err)81	uassert.Equal(t, 2, g.ScoreOf(alice))82	uassert.Equal(t, 2, g.Count())83	uassert.Equal(t, 1, g.People(), "one address has been vouched for")8485	// And revoking lowers it.86	_, err = g.Revoke(carol, alice)87	urequire.NoError(t, err)88	uassert.Equal(t, 1, g.ScoreOf(alice))89	uassert.Equal(t, 1, g.Count())90}9192func TestIsTrusted(t *testing.T) {93	g := NewGraph()94	g.Record(bob, alice, "one", 0, 100)95	g.Record(carol, alice, "two", 0, 100)9697	tests := []struct {98		name string99		addr address100		min  int101		want bool102	}{103		{"below the score", alice, 1, true},104		{"at the score", alice, 2, true},105		{"above the score", alice, 3, false},106		{"zero is read as one", alice, 0, true},107		{"zero does not open the gate for nobody", dave, 0, false},108		{"a negative min is read as one too", dave, -5, false},109		{"nobody vouches for dave", dave, 1, false},110	}111	for _, tt := range tests {112		uassert.Equal(t, tt.want, g.IsTrusted(tt.addr, tt.min), tt.name)113	}114}115116func TestTheBondFollowsTheVouchInBothDirections(t *testing.T) {117	g := NewGraph()118119	_, err := g.Record(bob, alice, "staking 1 GNOT on this", 1_000_000, 100)120	urequire.NoError(t, err)121	uassert.Equal(t, int64(1_000_000), g.BondedFor(alice))122	uassert.Equal(t, int64(1_000_000), g.TotalBonded())123	uassert.Equal(t, int64(0), g.Owed(bob), "nothing is owed while the vouch stands")124125	// A repeated vouch adds to the bond rather than replacing it.126	_, err = g.Record(bob, alice, "doubling down", 500_000, 101)127	urequire.NoError(t, err)128	uassert.Equal(t, int64(1_500_000), g.BondedFor(alice))129	uassert.Equal(t, int64(1_500_000), g.BondFrom(bob, alice))130131	// A second voucher's bond piles on top.132	g.Record(carol, alice, "and me", 250_000, 102)133	uassert.Equal(t, int64(1_750_000), g.BondedFor(alice))134135	// Revoking returns exactly what that voucher put in, and nothing else.136	refund, err := g.Revoke(bob, alice)137	urequire.NoError(t, err)138	uassert.Equal(t, int64(1_500_000), refund)139	uassert.Equal(t, int64(250_000), g.BondedFor(alice), "carol's bond stays")140	uassert.Equal(t, int64(250_000), g.TotalBonded())141	uassert.Equal(t, int64(1_500_000), g.Owed(bob))142	uassert.Equal(t, int64(1_500_000), g.TotalOwed())143144	// Withdrawing zeroes the credit before the realm sends anything.145	got, err := g.Withdraw(bob)146	urequire.NoError(t, err)147	uassert.Equal(t, int64(1_500_000), got)148	uassert.Equal(t, int64(0), g.Owed(bob))149	uassert.Equal(t, int64(0), g.TotalOwed())150151	// A reentrant withdrawal finds nothing.152	_, err = g.Withdraw(bob)153	uassert.ErrorIs(t, err, ErrNothingOwed)154}155156func TestRevokeRefusesWhatWasNeverThere(t *testing.T) {157	g := NewGraph()158	_, err := g.Revoke(alice, bob)159	uassert.ErrorIs(t, err, ErrNoVouch)160161	g.Record(alice, bob, "fine", 0, 100)162	_, err = g.Revoke(bob, alice)163	uassert.ErrorIs(t, err, ErrNoVouch, "a vouch is directed, so the other way round is not one")164}165166func TestTheTwoDirections(t *testing.T) {167	g := NewGraph()168	g.Record(alice, bob, "a to b", 0, 100)169	g.Record(alice, carol, "a to c", 0, 100)170	g.Record(bob, carol, "b to c", 0, 100)171172	uassert.Equal(t, 0, len(g.VouchedBy(alice)), "nobody vouches for alice")173	uassert.Equal(t, 2, len(g.VouchesOf(alice)))174	uassert.Equal(t, 2, len(g.VouchedBy(carol)))175	uassert.Equal(t, 0, len(g.VouchesOf(carol)))176177	// Sorted by address, so two identical calls render identically.178	in := g.VouchedBy(carol)179	uassert.True(t, in[0].String() < in[1].String(), "inbound is sorted")180181	uassert.Equal(t, "a to c", g.ReasonFrom(alice, carol))182	uassert.Equal(t, "", g.ReasonFrom(carol, alice), "no vouch, no reason")183}184185func TestMutual(t *testing.T) {186	g := NewGraph()187	g.Record(alice, bob, "a to b", 0, 100)188189	uassert.False(t, g.Mutual(alice, bob), "one direction is not mutual")190	uassert.False(t, g.Mutual(alice, alice), "an address is never mutual with itself")191192	g.Record(bob, alice, "b to a", 0, 101)193	uassert.True(t, g.Mutual(alice, bob))194	uassert.True(t, g.Mutual(bob, alice), "mutual is symmetric")195	uassert.False(t, g.Mutual(alice, carol))196197	// The cheapest sybil shape: two addresses, two transactions, both pass a198	// gate set at one. That is the whole reason Mutual is exported.199	uassert.True(t, g.IsTrusted(alice, 1))200	uassert.True(t, g.IsTrusted(bob, 1))201	uassert.False(t, g.IsTrusted(alice, 2))202}203204func TestLeaderboardIsATotalOrder(t *testing.T) {205	g := NewGraph()206	g.Record(alice, carol, "one", 0, 100)207	g.Record(bob, carol, "two", 0, 100)208	g.Record(alice, bob, "three", 100, 100)209210	rows := g.Leaderboard(10)211	urequire.Equal(t, 2, len(rows))212	uassert.Equal(t, carol.String(), rows[0].Addr.String(), "highest score first")213	uassert.Equal(t, 2, rows[0].Score)214	uassert.Equal(t, int64(100), rows[1].Bonded)215216	uassert.Equal(t, 1, len(g.Leaderboard(1)), "limit truncates")217	uassert.Equal(t, 0, len(g.Leaderboard(0)))218	uassert.Equal(t, 0, len(NewGraph().Leaderboard(10)), "an empty graph ranks nobody")219220	// Revoking the last vouch for an address drops it off the board.221	g.Revoke(alice, bob)222	uassert.Equal(t, 1, len(g.Leaderboard(10)))223	uassert.Equal(t, 0, g.ScoreOf(bob))224}225226func TestBadge(t *testing.T) {227	g := NewGraph()228	uassert.True(t, strings.Contains(Badge(realmPath, alice, g.ScoreOf(alice), 0), "not vouched for"))229230	g.Record(bob, alice, "one", 2_000_000, 100)231	got := Badge(realmPath, alice, g.ScoreOf(alice), g.BondedFor(alice))232	uassert.True(t, strings.Contains(got, "1 voucher]"), "singular: "+got)233	uassert.True(t, strings.Contains(got, "2 GNOT bonded"), got)234	uassert.True(t, strings.Contains(got, AddrURL(realmPath, alice)), got)235236	g.Record(carol, alice, "two", 0, 101)237	uassert.True(t, strings.Contains(Badge(realmPath, alice, g.ScoreOf(alice), 0), "2 vouchers]"))238}239240func TestURLsStripTheChainDomain(t *testing.T) {241	uassert.Equal(t, "/r/moul/x/social/vouch/v0", RealmURL(realmPath))242	uassert.Equal(t, "/r/a/b:addr/"+alice.String(), AddrURL("gno.land/r/a/b", alice))243}244

Functions

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

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