// SPDX-License-Identifier: EUPL-1.2 package lns import ( "bytes" "crypto/sha3" "encoding/binary" "strings" "testing" core "dappco.re/go/core" "golang.org/x/crypto/blake2b" "dappco.re/go/lns/pkg/covenant" dnspkg "dappco.re/go/lns/pkg/dns" "dappco.re/go/lns/pkg/primitives" ) func TestPackageResolveAndVerifyAliases(t *testing.T) { want := primitives.Hash(sha3.Sum256([]byte("foo-bar"))) resolveCases := []struct { name string input any fn func(any) (primitives.Hash, error) }{ {name: "Resolve", input: "Foo-Bar.lthn", fn: Resolve}, {name: "GetResolve", input: "Foo-Bar.lthn", fn: GetResolve}, {name: "Hash", input: "Foo-Bar.lthn", fn: Hash}, {name: "GetHash", input: "Foo-Bar.lthn", fn: GetHash}, {name: "GetResolveString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return GetResolveString(name.(string)) }}, {name: "GetResolveBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return GetResolveBinary(name.([]byte)) }}, {name: "ResolveName", input: "Foo-Bar.lthn", fn: ResolveName}, {name: "GetResolveName", input: "Foo-Bar.lthn", fn: GetResolveName}, {name: "HashName", input: "Foo-Bar.lthn", fn: HashName}, {name: "GetHashName", input: "Foo-Bar.lthn", fn: GetHashName}, {name: "ResolveByName", input: "Foo-Bar.lthn", fn: ResolveByName}, {name: "GetResolveByName", input: "Foo-Bar.lthn", fn: GetResolveByName}, {name: "HashByName", input: "Foo-Bar.lthn", fn: HashByName}, {name: "GetHashByName", input: "Foo-Bar.lthn", fn: GetHashByName}, {name: "ResolveByString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return ResolveByString(name.(string)) }}, {name: "GetResolveByString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return GetResolveByString(name.(string)) }}, {name: "ResolveByBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return ResolveByBinary(name.([]byte)) }}, {name: "GetResolveByBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return GetResolveByBinary(name.([]byte)) }}, {name: "HashString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return HashString(name.(string)) }}, {name: "GetHashString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return GetHashString(name.(string)) }}, {name: "HashBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return HashBinary(name.([]byte)) }}, {name: "GetHashBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return GetHashBinary(name.([]byte)) }}, {name: "HashByString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return HashByString(name.(string)) }}, {name: "GetHashByString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return GetHashByString(name.(string)) }}, {name: "HashByBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return HashByBinary(name.([]byte)) }}, {name: "GetHashByBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return GetHashByBinary(name.([]byte)) }}, } for _, tc := range resolveCases { got, err := tc.fn(tc.input) if err != nil { t.Fatalf("%s returned error: %v", tc.name, err) } if got != want { t.Fatalf("%s returned %x, want %x", tc.name, got, want) } } if got, err := ResolveString("Foo-Bar.lthn"); err != nil { t.Fatalf("ResolveString returned error: %v", err) } else if got != want { t.Fatalf("ResolveString returned %x, want %x", got, want) } if got, err := ResolveBinary([]byte("Foo-Bar.lthn")); err != nil { t.Fatalf("ResolveBinary returned error: %v", err) } else if got != want { t.Fatalf("ResolveBinary returned %x, want %x", got, want) } if _, err := Resolve(123); err == nil || !strings.Contains(err.Error(), "invalid name type") { t.Fatalf("Resolve should reject unsupported input types with an invalid name type error, got %v", err) } if _, err := Hash(123); err == nil || !strings.Contains(err.Error(), "invalid name type") { t.Fatalf("Hash should reject unsupported input types with an invalid name type error, got %v", err) } for _, tc := range []struct { name string fn func(any) (primitives.Hash, error) }{ {name: "ResolveString", fn: func(name any) (primitives.Hash, error) { return ResolveString(name.(string)) }}, {name: "ResolveBinary", fn: func(name any) (primitives.Hash, error) { return ResolveBinary(name.([]byte)) }}, {name: "HashString", fn: func(name any) (primitives.Hash, error) { return HashString(name.(string)) }}, {name: "HashBinary", fn: func(name any) (primitives.Hash, error) { return HashBinary(name.([]byte)) }}, } { input := any("foo.lthn..") if tc.name == "ResolveBinary" || tc.name == "HashBinary" { input = []byte("foo.lthn..") } if _, err := tc.fn(input); err == nil { t.Fatalf("%s should reject malformed names", tc.name) } } if got, err := HashString("Foo-Bar.lthn"); err != nil { t.Fatalf("HashString returned error: %v", err) } else if got != want { t.Fatalf("HashString returned %x, want %x", got, want) } if got, err := HashBinary([]byte("Foo-Bar.lthn")); err != nil { t.Fatalf("HashBinary returned error: %v", err) } else if got != want { t.Fatalf("HashBinary returned %x, want %x", got, want) } verifyCases := []struct { name string input any badInput any fn func(any) bool }{ {name: "Verify", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: Verify}, {name: "GetVerify", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: GetVerify}, {name: "VerifyName", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: VerifyName}, {name: "GetVerifyName", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: GetVerifyName}, {name: "VerifyByName", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: VerifyByName}, {name: "GetVerifyByName", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: GetVerifyByName}, {name: "VerifyString", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: func(name any) bool { return VerifyString(name.(string)) }}, {name: "GetVerifyString", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: func(name any) bool { return GetVerifyString(name.(string)) }}, {name: "VerifyBinary", input: []byte("Foo-Bar.lthn"), badInput: []byte("foo.bar.lthn"), fn: func(name any) bool { return VerifyBinary(name.([]byte)) }}, {name: "GetVerifyBinary", input: []byte("Foo-Bar.lthn"), badInput: []byte("foo.bar.lthn"), fn: func(name any) bool { return GetVerifyBinary(name.([]byte)) }}, {name: "VerifyByString", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: func(name any) bool { return VerifyByString(name.(string)) }}, {name: "GetVerifyByString", input: "Foo-Bar.lthn", badInput: "foo.bar.lthn", fn: func(name any) bool { return GetVerifyByString(name.(string)) }}, {name: "VerifyByBinary", input: []byte("Foo-Bar.lthn"), badInput: []byte("foo.bar.lthn"), fn: func(name any) bool { return VerifyByBinary(name.([]byte)) }}, {name: "GetVerifyByBinary", input: []byte("Foo-Bar.lthn"), badInput: []byte("foo.bar.lthn"), fn: func(name any) bool { return GetVerifyByBinary(name.([]byte)) }}, } for _, tc := range verifyCases { if !tc.fn(tc.input) { t.Fatalf("%s should accept canonical names", tc.name) } if tc.fn(tc.badInput) { t.Fatalf("%s should reject malformed names", tc.name) } } if !VerifyString("Foo-Bar.lthn") { t.Fatal("VerifyString should accept canonical names") } if VerifyString("foo.bar.lthn") { t.Fatal("VerifyString should reject malformed names") } if !VerifyBinary([]byte("Foo-Bar.lthn")) { t.Fatal("VerifyBinary should accept canonical names") } if VerifyBinary([]byte("foo.bar.lthn")) { t.Fatal("VerifyBinary should reject malformed names") } if !VerifyByString("Foo-Bar.lthn") { t.Fatal("VerifyByString should accept canonical names") } if !VerifyByBinary([]byte("Foo-Bar.lthn")) { t.Fatal("VerifyByBinary should accept canonical names") } if !GetHasReserved("RESERVED.lthn") { t.Fatal("GetHasReserved should report reserved canonical names") } if !GetHasReservedString("reserved") { t.Fatal("GetHasReservedString should report reserved catalog labels") } if !GetHasReservedBinary([]byte("reserved")) { t.Fatal("GetHasReservedBinary should report reserved catalog labels") } if !GetHasLocked("NEC.lthn") { t.Fatal("GetHasLocked should report locked canonical names") } if !GetHasLockedString("nec") { t.Fatal("GetHasLockedString should report locked catalog labels") } if !GetHasLockedBinary([]byte("nec")) { t.Fatal("GetHasLockedBinary should report locked catalog labels") } reservedHash, err := covenant.HashString("reserved") if err != nil { t.Fatalf("HashString returned error: %v", err) } if !GetHasReservedHash(reservedHash) { t.Fatal("GetHasReservedHash should report reserved canonical hashes") } lockedHash, err := covenant.HashString("nec") if err != nil { t.Fatalf("HashString returned error: %v", err) } if !GetHasLockedHash(lockedHash) { t.Fatal("GetHasLockedHash should report locked canonical hashes") } } func TestPackageBlindAndTypeAliases(t *testing.T) { var nonce primitives.Hash for i := range nonce { nonce[i] = byte(i) } got, err := Blind(0x1122334455667788, nonce) if err != nil { t.Fatalf("Blind returned error: %v", err) } var input [40]byte binary.LittleEndian.PutUint64(input[:8], 0x1122334455667788) copy(input[8:], nonce[:]) want := blake2b.Sum256(input[:]) if got != want { t.Fatalf("Blind returned %x, want %x", got, want) } got, err = GetBlind(0x1122334455667788, nonce) if err != nil { t.Fatalf("GetBlind returned error: %v", err) } if got != want { t.Fatalf("GetBlind returned %x, want %x", got, want) } if got := TypeName(covenant.TypeBid); got != "BID" { t.Fatalf("TypeName(TypeBid) = %q, want %q", got, "BID") } if got := GetTypeName(covenant.CovenantType(99)); got != "UNKNOWN" { t.Fatalf("GetTypeName(99) = %q, want %q", got, "UNKNOWN") } } func TestPackageClaimAlias(t *testing.T) { if NewClaim() == nil { t.Fatal("NewClaim should return a claim wrapper") } if GetNewClaim() == nil { t.Fatal("GetNewClaim should alias NewClaim") } if view := NewNameView(); view == nil { t.Fatal("NewNameView should return a cached name-state view") } else if view.ToNameUndo().Names != nil { t.Fatal("NewNameView should start with an empty undo payload") } if GetNewNameView() == nil { t.Fatal("GetNewNameView should alias NewNameView") } } func TestPackageNSECAliases(t *testing.T) { if got := NextName("ExAmPle."); got != "example\x00." { t.Fatalf("NextName returned %q", got) } if got := GetNextName("ExAmPle."); got != "example\x00." { t.Fatalf("GetNextName returned %q", got) } if got := PrevName("ExAmPle."); got != "exampld\xff." { t.Fatalf("PrevName returned %q", got) } if got := GetPrevName("ExAmPle."); got != "exampld\xff." { t.Fatalf("GetPrevName returned %q", got) } record := Create(".", NextName("."), TYPE_MAP_ROOT) if record.Name != "." { t.Fatalf("Create Name = %q, want %q", record.Name, ".") } if record.NextDomain != "\x00." { t.Fatalf("Create NextDomain = %q, want %q", record.NextDomain, "\x00.") } if record.TTL != DEFAULT_TTL { t.Fatalf("Create TTL = %d, want %d", record.TTL, DEFAULT_TTL) } if !bytes.Equal(record.TypeBitmap, TYPE_MAP_ROOT) { t.Fatalf("Create TypeBitmap = %x, want %x", record.TypeBitmap, TYPE_MAP_ROOT) } record.TypeBitmap[0] ^= 0xff if bytes.Equal(record.TypeBitmap, TYPE_MAP_ROOT) { t.Fatal("Create should copy the type bitmap") } alias := GetCreate("foo", "bar", TYPE_MAP_NS) if alias.Name != "foo." || alias.NextDomain != "bar." || alias.TTL != DEFAULT_TTL { t.Fatalf("GetCreate returned %+v", alias) } if !bytes.Equal(alias.TypeBitmap, TYPE_MAP_NS) { t.Fatalf("GetCreate TypeBitmap = %x, want %x", alias.TypeBitmap, TYPE_MAP_NS) } } func TestPackageRolloutAlias(t *testing.T) { var hash primitives.Hash rules := NameRules{ AuctionStart: 100, RolloutInterval: 7, } if !HasRollout(hash, 100, rules) { t.Fatal("HasRollout should accept the rollout start height") } if !GetHasRollout(hash, 100, rules) { t.Fatal("GetHasRollout should alias HasRollout") } } func TestPackageServiceNameAlias(t *testing.T) { if got := GetServiceName(); got != ServiceName { t.Fatalf("GetServiceName() = %q, want %q", got, ServiceName) } } func TestPackageServiceConstructorAliases(t *testing.T) { if got := GetNewService(nil); got == nil { t.Fatal("GetNewService should return a service instance") } c := core.New() svc := GetNewServiceWithOptions(WithCore(c)) if svc == nil { t.Fatal("GetNewServiceWithOptions should return a service instance") } if svc.Core() != c { t.Fatal("GetNewServiceWithOptions should attach the provided Core instance") } } func TestPackageServiceOptionAliases(t *testing.T) { c := core.New() reserved := &covenant.ReservedCatalog{} locked := &covenant.LockedCatalog{} svc := NewServiceWithOptions( GetWithCore(c), GetWithReservedCatalog(reserved), GetWithLockedCatalog(locked), ) if svc.Core() != c { t.Fatal("GetWithCore should alias WithCore") } if svc.ReservedCatalog() != reserved { t.Fatal("GetWithReservedCatalog should alias WithReservedCatalog") } if svc.LockedCatalog() != locked { t.Fatal("GetWithLockedCatalog should alias WithLockedCatalog") } } func TestPackageRegisterAliases(t *testing.T) { if res := GetRegister(nil); res.OK { t.Fatal("GetRegister(nil) should fail") } c := core.New() if res := GetRegister(c); !res.OK { t.Fatal("GetRegister should register a service for a valid Core") } else if svc, ok := res.Value.(*Service); !ok || svc == nil { t.Fatalf("GetRegister returned %T, want *Service", res.Value) } reserved := &covenant.ReservedCatalog{} locked := &covenant.LockedCatalog{} app := core.New( core.WithService(GetRegisterWithOptions( WithReservedCatalog(reserved), WithLockedCatalog(locked), )), ) svc, ok := core.ServiceFor[*Service](app, ServiceName) if !ok { t.Fatal("GetRegisterWithOptions should register the LNS service") } if svc.ReservedCatalog() != reserved { t.Fatal("GetRegisterWithOptions should apply the reserved catalog override") } if svc.LockedCatalog() != locked { t.Fatal("GetRegisterWithOptions should apply the locked catalog override") } } func TestPackageDefaultCatalogAliases(t *testing.T) { if DefaultReservedCatalog() != ReservedCatalog() { t.Fatal("DefaultReservedCatalog should alias ReservedCatalog") } if GetReservedCatalog() != ReservedCatalog() { t.Fatal("GetReservedCatalog should alias ReservedCatalog") } if GetDefaultReservedCatalog() != DefaultReservedCatalog() { t.Fatal("GetDefaultReservedCatalog should alias DefaultReservedCatalog") } if DefaultLockedCatalog() != LockedCatalog() { t.Fatal("DefaultLockedCatalog should alias LockedCatalog") } if GetLockedCatalog() != LockedCatalog() { t.Fatal("GetLockedCatalog should alias LockedCatalog") } if GetDefaultLockedCatalog() != DefaultLockedCatalog() { t.Fatal("GetDefaultLockedCatalog should alias DefaultLockedCatalog") } if !HasReservedCatalog() || !GetHasReservedCatalog() { t.Fatal("HasReservedCatalog aliases should report the package reserved catalog") } if !HasLockedCatalog() || !GetHasLockedCatalog() { t.Fatal("HasLockedCatalog aliases should report the package locked catalog") } } func TestPackageDefaultCatalogsIgnoreOverrides(t *testing.T) { origReserved := Reserved origLocked := Locked defer func() { Reserved = origReserved Locked = origLocked }() Reserved = &covenant.ReservedCatalog{} Locked = &covenant.LockedCatalog{} if DefaultReservedCatalog() != covenant.DefaultReservedCatalog() { t.Fatal("DefaultReservedCatalog should return the canonical reserved catalog") } if DefaultReservedCatalog() == ReservedCatalog() { t.Fatal("DefaultReservedCatalog should ignore the mutable Reserved variable") } if GetReservedCatalog() != ReservedCatalog() { t.Fatal("GetReservedCatalog should track the mutable Reserved variable") } if DefaultLockedCatalog() != covenant.DefaultLockedCatalog() { t.Fatal("DefaultLockedCatalog should return the canonical locked catalog") } if DefaultLockedCatalog() == LockedCatalog() { t.Fatal("DefaultLockedCatalog should ignore the mutable Locked variable") } if GetLockedCatalog() != LockedCatalog() { t.Fatal("GetLockedCatalog should track the mutable Locked variable") } } func TestPackageCatalogFallbacksWhenNil(t *testing.T) { origReserved := Reserved origLocked := Locked defer func() { Reserved = origReserved Locked = origLocked }() Reserved = nil Locked = nil if ReservedCatalog() == nil { t.Fatal("ReservedCatalog should fall back to the canonical catalog when nil") } if LockedCatalog() == nil { t.Fatal("LockedCatalog should fall back to the canonical catalog when nil") } if !HasReservedCatalog() || !GetHasReservedCatalog() { t.Fatal("HasReservedCatalog aliases should keep working when Reserved is nil") } if !HasLockedCatalog() || !GetHasLockedCatalog() { t.Fatal("HasLockedCatalog aliases should keep working when Locked is nil") } if _, ok := GetReservedName("RESERVED"); !ok { t.Fatal("GetReservedName should keep working when Reserved is nil") } if _, ok := GetLockedName("NEC"); !ok { t.Fatal("GetLockedName should keep working when Locked is nil") } svc := NewService(nil) if svc.ReservedCatalog() == nil { t.Fatal("service ReservedCatalog should fall back to the canonical catalog when nil") } if svc.LockedCatalog() == nil { t.Fatal("service LockedCatalog should fall back to the canonical catalog when nil") } if _, ok := svc.GetReservedName("RESERVED"); !ok { t.Fatal("service GetReservedName should keep working when Reserved is nil") } if _, ok := svc.GetLockedName("NEC"); !ok { t.Fatal("service GetLockedName should keep working when Locked is nil") } } func TestLookupCatalogNamePreservesDottedLabels(t *testing.T) { hash := primitives.Hash(sha3.Sum256([]byte("foo.bar"))) byLabel := map[string]string{ "foo.bar": "label", } byHash := map[primitives.Hash]string{ hash: "hash", } got, ok := lookupCatalogName( "foo.bar", func(name string) (string, bool) { item, ok := byLabel[name] return item, ok }, func(name primitives.Hash) (string, bool) { item, ok := byHash[name] return item, ok }, ) if !ok || got != "label" { t.Fatalf("lookupCatalogName should resolve dotted raw labels, got %q ok=%v", got, ok) } got, ok = lookupCatalogName( "FOO.BAR.LTHN", func(name string) (string, bool) { item, ok := byLabel[name] return item, ok }, func(name primitives.Hash) (string, bool) { item, ok := byHash[name] return item, ok }, ) if !ok || got != "hash" { t.Fatalf("lookupCatalogName should fall back to hash lookup for canonical dotted names, got %q ok=%v", got, ok) } got, ok = LookupCatalogName( "foo.bar", func(name string) (string, bool) { item, ok := byLabel[name] return item, ok }, func(name primitives.Hash) (string, bool) { item, ok := byHash[name] return item, ok }, ) if !ok || got != "label" { t.Fatalf("LookupCatalogName should preserve dotted labels, got %q ok=%v", got, ok) } got, ok = GetLookupCatalogName( "FOO.BAR.LTHN", func(name string) (string, bool) { item, ok := byLabel[name] return item, ok }, func(name primitives.Hash) (string, bool) { item, ok := byHash[name] return item, ok }, ) if !ok || got != "hash" { t.Fatalf("GetLookupCatalogName should alias LookupCatalogName, got %q ok=%v", got, ok) } if !HasLookupCatalogName( "foo.bar", func(name string) (string, bool) { item, ok := byLabel[name] return item, ok }, func(name primitives.Hash) (string, bool) { item, ok := byHash[name] return item, ok }, ) { t.Fatal("HasLookupCatalogName should report a dotted raw label match") } if !GetHasLookupCatalogName( "FOO.BAR.LTHN", func(name string) (string, bool) { item, ok := byLabel[name] return item, ok }, func(name primitives.Hash) (string, bool) { item, ok := byHash[name] return item, ok }, ) { t.Fatal("GetHasLookupCatalogName should alias HasLookupCatalogName") } svc := NewService(nil) gotAny, ok := svc.LookupCatalogName( "foo.bar", func(name string) (any, bool) { item, ok := byLabel[name] return any(item), ok }, func(name primitives.Hash) (any, bool) { item, ok := byHash[name] return any(item), ok }, ) if !ok { t.Fatalf("svc.LookupCatalogName should resolve dotted raw labels, got ok=%v", ok) } gotStr, ok := gotAny.(string) if !ok || gotStr != "label" { t.Fatalf("svc.LookupCatalogName should resolve dotted raw labels, got %v ok=%v", gotAny, ok) } gotAny, ok = svc.GetLookupCatalogName( "FOO.BAR.LTHN", func(name string) (any, bool) { item, ok := byLabel[name] return any(item), ok }, func(name primitives.Hash) (any, bool) { item, ok := byHash[name] return any(item), ok }, ) if !ok { t.Fatalf("svc.GetLookupCatalogName should alias svc.LookupCatalogName, got ok=%v", ok) } gotStr, ok = gotAny.(string) if !ok || gotStr != "hash" { t.Fatalf("svc.GetLookupCatalogName should alias svc.LookupCatalogName, got %v ok=%v", gotAny, ok) } if !svc.HasLookupCatalogName( "foo.bar", func(name string) (any, bool) { item, ok := byLabel[name] return any(item), ok }, func(name primitives.Hash) (any, bool) { item, ok := byHash[name] return any(item), ok }, ) { t.Fatal("svc.HasLookupCatalogName should report a dotted raw label match") } if !svc.GetHasLookupCatalogName( "FOO.BAR.LTHN", func(name string) (any, bool) { item, ok := byLabel[name] return any(item), ok }, func(name primitives.Hash) (any, bool) { item, ok := byHash[name] return any(item), ok }, ) { t.Fatal("svc.GetHasLookupCatalogName should alias svc.HasLookupCatalogName") } } func TestLookupCatalogNameNilCallbacks(t *testing.T) { if got, ok := LookupCatalogName[string]("foo", nil, nil); ok || got != "" { t.Fatalf("LookupCatalogName with nil callbacks = (%q, %v), want (\"\", false)", got, ok) } if got, ok := GetLookupCatalogName[string]("foo", nil, nil); ok || got != "" { t.Fatalf("GetLookupCatalogName with nil callbacks = (%q, %v), want (\"\", false)", got, ok) } if ok := HasLookupCatalogName[string]("foo", nil, nil); ok { t.Fatal("HasLookupCatalogName with nil callbacks should return false") } if ok := GetHasLookupCatalogName[string]("foo", nil, nil); ok { t.Fatal("GetHasLookupCatalogName with nil callbacks should return false") } svc := NewService(nil) if got, ok := svc.LookupCatalogName("foo", nil, nil); ok || got != nil { t.Fatalf("svc.LookupCatalogName with nil callbacks = (%v, %v), want (nil, false)", got, ok) } if got, ok := svc.GetLookupCatalogName("foo", nil, nil); ok || got != nil { t.Fatalf("svc.GetLookupCatalogName with nil callbacks = (%v, %v), want (nil, false)", got, ok) } if ok := svc.HasLookupCatalogName("foo", nil, nil); ok { t.Fatal("svc.HasLookupCatalogName with nil callbacks should return false") } if ok := svc.GetHasLookupCatalogName("foo", nil, nil); ok { t.Fatal("svc.GetHasLookupCatalogName with nil callbacks should return false") } } func TestPackageTypeTables(t *testing.T) { if len(Types) == 0 || len(TypesByVal) == 0 { t.Fatal("type lookup tables should not be empty") } if len(GetTypes()) != len(Types) { t.Fatal("GetTypes should alias Types") } if len(GetTypesByVal()) != len(TypesByVal) { t.Fatal("GetTypesByVal should alias TypesByVal") } if got, ok := Types["BID"]; !ok || got != covenant.TypeBid { t.Fatalf("Types[\"BID\"] = %d, want %d", got, covenant.TypeBid) } if got, ok := TypesByVal[covenant.TypeBid]; !ok || got != "BID" { t.Fatalf("TypesByVal[TypeBid] = %q, want %q", got, "BID") } if got, ok := GetTypes()["BID"]; !ok || got != covenant.TypeBid { t.Fatalf("GetTypes()[\"BID\"] = %d, want %d", got, covenant.TypeBid) } if got, ok := GetTypesByVal()[covenant.TypeBid]; !ok || got != "BID" { t.Fatalf("GetTypesByVal()[TypeBid] = %q, want %q", got, "BID") } if CovenantType(TypeBid) != covenant.TypeBid { t.Fatalf("CovenantType(TypeBid) = %d, want %d", CovenantType(TypeBid), covenant.TypeBid) } } func TestPackageBlacklistAlias(t *testing.T) { if len(Blacklist) == 0 { t.Fatal("Blacklist should not be empty") } if len(GetBlacklist()) != len(Blacklist) { t.Fatal("GetBlacklist should alias Blacklist") } if _, ok := GetBlacklist()["test"]; !ok { t.Fatal("GetBlacklist should expose the covenant verifier blacklist") } } func TestPackageVerificationFlagTables(t *testing.T) { if len(VerificationFlags) != 3 { t.Fatalf("VerificationFlags has %d entries, want 3", len(VerificationFlags)) } if len(GetVerificationFlags()) != len(VerificationFlags) { t.Fatal("GetVerificationFlags should alias VerificationFlags") } if len(VerificationFlagsByVal) != len(VerificationFlags) { t.Fatalf("VerificationFlagsByVal has %d entries, want %d", len(VerificationFlagsByVal), len(VerificationFlags)) } if len(GetVerificationFlagsByVal()) != len(VerificationFlagsByVal) { t.Fatal("GetVerificationFlagsByVal should alias VerificationFlagsByVal") } if got, ok := VerificationFlags["VERIFY_COVENANTS_LOCKUP"]; !ok || got != covenant.VerifyCovenantsLockup { t.Fatalf("VerificationFlags[LOCKUP] = %d, want %d", got, covenant.VerifyCovenantsLockup) } if got, ok := VerificationFlagsByVal[covenant.VerifyCovenantsNone]; !ok || got != "VERIFY_COVENANTS_NONE" { t.Fatalf("VerificationFlagsByVal[None] = %q, want %q", got, "VERIFY_COVENANTS_NONE") } } func TestPackageConstantGetters(t *testing.T) { if GetMaxNameSize() != MaxNameSize { t.Fatalf("GetMaxNameSize() = %d, want %d", GetMaxNameSize(), MaxNameSize) } if GetMaxResourceSize() != MaxResourceSize { t.Fatalf("GetMaxResourceSize() = %d, want %d", GetMaxResourceSize(), MaxResourceSize) } if GetVerifyCovenantsNone() != VerifyCovenantsNone { t.Fatalf("GetVerifyCovenantsNone() = %d, want %d", GetVerifyCovenantsNone(), VerifyCovenantsNone) } if GetVerifyCovenantsHardened() != VerifyCovenantsHardened { t.Fatalf("GetVerifyCovenantsHardened() = %d, want %d", GetVerifyCovenantsHardened(), VerifyCovenantsHardened) } if GetVerifyCovenantsLockup() != VerifyCovenantsLockup { t.Fatalf("GetVerifyCovenantsLockup() = %d, want %d", GetVerifyCovenantsLockup(), VerifyCovenantsLockup) } if GetMandatoryVerifyCovenantFlags() != MandatoryVerifyCovenantFlags { t.Fatalf("GetMandatoryVerifyCovenantFlags() = %d, want %d", GetMandatoryVerifyCovenantFlags(), MandatoryVerifyCovenantFlags) } if GetMaxCovenantSize() != MaxCovenantSize { t.Fatalf("GetMaxCovenantSize() = %d, want %d", GetMaxCovenantSize(), MaxCovenantSize) } if GetCovenantMaxSize() != CovenantMaxSize { t.Fatalf("GetCovenantMaxSize() = %d, want %d", GetCovenantMaxSize(), CovenantMaxSize) } } func TestPackageDNSCommonGetters(t *testing.T) { if GetDefaultTTL() != DEFAULT_TTL { t.Fatalf("GetDefaultTTL() = %d, want %d", GetDefaultTTL(), DEFAULT_TTL) } if !bytes.Equal(GetDummy(), DUMMY) { t.Fatal("GetDummy should alias DUMMY") } if !bytes.Equal(GetTypeMapRoot(), TYPE_MAP_ROOT) { t.Fatal("GetTypeMapRoot should alias TYPE_MAP_ROOT") } if !bytes.Equal(GetTypeMapEmpty(), TYPE_MAP_EMPTY) { t.Fatal("GetTypeMapEmpty should alias TYPE_MAP_EMPTY") } if !bytes.Equal(GetTypeMapNS(), TYPE_MAP_NS) { t.Fatal("GetTypeMapNS should alias TYPE_MAP_NS") } if !bytes.Equal(GetTypeMapTXT(), TYPE_MAP_TXT) { t.Fatal("GetTypeMapTXT should alias TYPE_MAP_TXT") } if !bytes.Equal(GetTypeMapA(), TYPE_MAP_A) { t.Fatal("GetTypeMapA should alias TYPE_MAP_A") } if !bytes.Equal(GetTypeMapAAAA(), TYPE_MAP_AAAA) { t.Fatal("GetTypeMapAAAA should alias TYPE_MAP_AAAA") } if len(GetHSTypes()) != len(HSTypes) { t.Fatal("GetHSTypes should alias HSTypes") } if len(GetHSTypesByVal()) != len(HSTypesByVal) { t.Fatal("GetHSTypesByVal should alias HSTypesByVal") } if got := GetHSTypes()["DS"]; got != dnspkg.HSTypeDS { t.Fatalf("GetHSTypes()[\"DS\"] = %d, want %d", got, dnspkg.HSTypeDS) } if got := GetHSTypesByVal()[dnspkg.HSTypeTXT]; got != "TXT" { t.Fatalf("GetHSTypesByVal()[TXT] = %q, want %q", got, "TXT") } } func TestPackageResourceAliases(t *testing.T) { _ = ResourceJSON{} _ = DNSMessage{} _ = DSRecordJSON{} _ = NSRecordJSON{} _ = GLUE4RecordJSON{} _ = GLUE6RecordJSON{} _ = SYNTH4RecordJSON{} _ = SYNTH6RecordJSON{} _ = TXTRecordJSON{} resource := NewResource() if resource == nil { t.Fatal("NewResource should return a resource") } if resource.TTL != DEFAULT_TTL { t.Fatalf("NewResource TTL = %d, want %d", resource.TTL, DEFAULT_TTL) } if GetNewResource() == nil { t.Fatal("GetNewResource should return a resource") } resource.Records = []ResourceRecord{ DSRecord{Digest: []byte{1, 2, 3}}, NSRecord{NS: "ns1.example"}, TXTRecord{Entries: []string{"hello", "world"}}, } if !resource.HasNS() || !resource.GetHasNS() { t.Fatal("resource aliases should expose HasNS helpers from pkg/dns") } if resource.HasType(dnspkg.HSTypeTXT) == false { t.Fatal("resource aliases should expose record type helpers from pkg/dns") } nsec := resource.ToNSEC("Example") if nsec.Name != "Example." || nsec.NextDomain != "example\x00." || nsec.TTL != DEFAULT_TTL { t.Fatalf("Resource.ToNSEC returned %+v", nsec) } encoded, err := resource.Encode() if err != nil { t.Fatalf("Resource.Encode returned error: %v", err) } decoded, err := DecodeResource(encoded) if err != nil { t.Fatalf("DecodeResource returned error: %v", err) } if decoded.TTL != DEFAULT_TTL { t.Fatalf("DecodeResource TTL = %d, want %d", decoded.TTL, DEFAULT_TTL) } if len(decoded.Records) != len(resource.Records) { t.Fatalf("DecodeResource records = %d, want %d", len(decoded.Records), len(resource.Records)) } if got, err := GetDecodeResource(encoded); err != nil { t.Fatalf("GetDecodeResource returned error: %v", err) } else if got.TTL != decoded.TTL || len(got.Records) != len(decoded.Records) { t.Fatal("GetDecodeResource should alias DecodeResource") } } func TestPackageNameStateAliases(t *testing.T) { var state NameState var delta NameDelta var rules NameStateRules if state.Name != nil { t.Fatal("NameState alias should expose the primitives name state type") } if !delta.IsNull() { t.Fatal("NameDelta alias should expose the primitives name delta type") } if NameStateOpening != primitives.NameStateOpening || NameStateLocked != primitives.NameStateLocked || NameStateBidding != primitives.NameStateBidding || NameStateReveal != primitives.NameStateReveal || NameStateClosed != primitives.NameStateClosed || NameStateRevoked != primitives.NameStateRevoked { t.Fatal("name state constants should mirror pkg/primitives") } if got := NameStateStatus(NameStateClosed).String(); got != "CLOSED" { t.Fatalf("NameStateStatus.String() = %q, want %q", got, "CLOSED") } if rules.TreeInterval != 0 || rules.LockupPeriod != 0 || rules.BiddingPeriod != 0 || rules.RevealPeriod != 0 { t.Fatal("NameStateRules alias should expose the primitives name state rules type") } } func TestPackageCatalogTypeAliases(t *testing.T) { var reserved ReservedName var locked LockedName var reservedEntry ReservedEntry var lockedEntry LockedEntry if reserved.Name != "" || locked.Name != "" { t.Fatal("catalog name aliases should expose the covenant catalog entry types") } if reservedEntry.Value.Name != "" || lockedEntry.Value.Name != "" { t.Fatal("catalog entry aliases should expose the covenant catalog entry pair types") } } func TestPackageNameSetHelpers(t *testing.T) { hash, err := HashString("example-name") if err != nil { t.Fatalf("HashString returned error: %v", err) } tx := primitives.Transaction{ Outputs: []primitives.Output{ {Covenant: primitives.Covenant{Type: uint8(covenant.TypeOpen), Items: [][]byte{hash[:], []byte{0, 0, 0, 0}, []byte("example-name")}}}, }, } set := map[primitives.Hash]struct{}{ hash: {}, } if !HasNames(tx, set) { t.Fatal("HasNames should report a matching name hash") } if !GetHasNames(tx, set) { t.Fatal("GetHasNames should report a matching name hash") } RemoveNames(tx, set) GetRemoveNames(tx, set) if len(set) != 0 { t.Fatalf("RemoveNames/GetRemoveNames left %d entries, want 0", len(set)) } AddNames(tx, set) GetAddNames(tx, set) if len(set) != 1 { t.Fatalf("AddNames/GetAddNames left %d entries, want 1", len(set)) } } func TestPackageHasSaneCovenants(t *testing.T) { hash, err := HashString("example-name") if err != nil { t.Fatalf("HashString returned error: %v", err) } valid := primitives.Transaction{ Inputs: []primitives.Input{ {Prevout: primitives.Outpoint{TxHash: primitives.Hash{1}, Index: 0}}, }, Outputs: []primitives.Output{ {Covenant: primitives.Covenant{Type: uint8(covenant.TypeOpen), Items: [][]byte{hash[:], []byte{0, 0, 0, 0}, []byte("example-name")}}}, }, } if !HasSaneCovenants(valid) { t.Fatal("HasSaneCovenants should accept structurally valid covenants") } if !GetHasSaneCovenants(valid) { t.Fatal("GetHasSaneCovenants should accept structurally valid covenants") } } func TestPackageGrindAndCountHelpers(t *testing.T) { tx := primitives.Transaction{ Outputs: []primitives.Output{ {Covenant: primitives.Covenant{Type: uint8(covenant.TypeOpen)}}, {Covenant: primitives.Covenant{Type: uint8(covenant.TypeClaim)}}, {Covenant: primitives.Covenant{Type: uint8(covenant.TypeUpdate)}}, {Covenant: primitives.Covenant{Type: uint8(covenant.TypeTransfer)}}, {Covenant: primitives.Covenant{Type: uint8(covenant.TypeRevoke)}}, {Covenant: primitives.Covenant{Type: uint8(covenant.TypeRegister)}}, {Covenant: primitives.Covenant{Type: uint8(covenant.TypeRenew)}}, {Covenant: primitives.Covenant{Type: uint8(covenant.TypeFinalize)}}, }, } if got := CountOpens(tx); got != 1 { t.Fatalf("CountOpens() = %d, want 1", got) } if got := GetCountOpens(tx); got != 1 { t.Fatalf("GetCountOpens() = %d, want 1", got) } if got := CountUpdates(tx); got != 5 { t.Fatalf("CountUpdates() = %d, want 5", got) } if got := GetCountUpdates(tx); got != 5 { t.Fatalf("GetCountUpdates() = %d, want 5", got) } if got := CountRenewals(tx); got != 3 { t.Fatalf("CountRenewals() = %d, want 3", got) } if got := GetCountRenewals(tx); got != 3 { t.Fatalf("GetCountRenewals() = %d, want 3", got) } rules := NameRules{ AuctionStart: 100, RolloutInterval: 7, } name, err := GrindName(8, 100, rules) if err != nil { t.Fatalf("GrindName returned error: %v", err) } alias, err := GetGrindName(8, 100, rules) if err != nil { t.Fatalf("GetGrindName returned error: %v", err) } if len(name) != 8 { t.Fatalf("GrindName returned %q with length %d, want 8", name, len(name)) } if len(alias) != 8 { t.Fatalf("GetGrindName returned %q with length %d, want 8", alias, len(alias)) } hash, err := covenant.HashString(name) if err != nil { t.Fatalf("HashString returned error: %v", err) } if !HasRollout(hash, 100, rules) { t.Fatalf("GrindName returned %q that does not satisfy rollout", name) } } func TestPackageRolloutHelpers(t *testing.T) { var hash primitives.Hash rules := NameRules{ AuctionStart: 100, RolloutInterval: 7, ClaimPeriod: 100, AlexaLockupPeriod: 250, } start, week := GetRollout(hash, rules) if start != 100 || week != 0 { t.Fatalf("GetRollout(zero) = (%d, %d), want (100, 0)", start, week) } if !HasRollout(hash, 100, rules) { t.Fatal("HasRollout should accept the rollout start height") } hash, err := covenant.HashString("reserved") if err != nil { t.Fatalf("HashString returned error: %v", err) } if !IsReserved(hash, 99, rules) { t.Fatal("IsReserved should report reserved hashes before the claim period") } if !GetIsReserved(hash, 99, rules) { t.Fatal("GetIsReserved should alias IsReserved") } rootHash, err := covenant.HashString("nec") if err != nil { t.Fatalf("HashString returned error: %v", err) } if !IsLockedUp(rootHash, 100, rules) { t.Fatal("IsLockedUp should keep root hashes locked after the claim period") } if !GetIsLockedUp(rootHash, 100, rules) { t.Fatal("GetIsLockedUp should alias IsLockedUp") } } func TestPackageCatalogLookupNormalization(t *testing.T) { reservedCases := []struct { name string fn func(any) (covenant.ReservedName, bool) }{ {name: "GetReserved", fn: GetReserved}, {name: "GetReservedName", fn: GetReservedName}, {name: "GetReservedString", fn: func(name any) (covenant.ReservedName, bool) { return GetReservedString(name.(string)) }}, {name: "GetReservedBinary", fn: func(name any) (covenant.ReservedName, bool) { return GetReservedBinary(name.([]byte)) }}, {name: "GetReservedByString", fn: func(name any) (covenant.ReservedName, bool) { return GetReservedByString(name.(string)) }}, {name: "GetReservedByBinary", fn: func(name any) (covenant.ReservedName, bool) { return GetReservedByBinary(name.([]byte)) }}, } for _, tc := range reservedCases { input := any("reserved.lthn.") if tc.name == "GetReservedBinary" || tc.name == "GetReservedByBinary" { input = []byte("reserved.lthn.") } item, ok := tc.fn(input) if !ok { t.Fatalf("%s should resolve canonicalized reserved names", tc.name) } if item.Name != "reserved" { t.Fatalf("%s returned %q, want %q", tc.name, item.Name, "reserved") } } lockedCases := []struct { name string fn func(any) (covenant.LockedName, bool) }{ {name: "GetLocked", fn: GetLocked}, {name: "GetLockedName", fn: GetLockedName}, {name: "GetLockedString", fn: func(name any) (covenant.LockedName, bool) { return GetLockedString(name.(string)) }}, {name: "GetLockedBinary", fn: func(name any) (covenant.LockedName, bool) { return GetLockedBinary(name.([]byte)) }}, {name: "GetLockedByString", fn: func(name any) (covenant.LockedName, bool) { return GetLockedByString(name.(string)) }}, {name: "GetLockedByBinary", fn: func(name any) (covenant.LockedName, bool) { return GetLockedByBinary(name.([]byte)) }}, } for _, tc := range lockedCases { input := any("nec.lthn.") if tc.name == "GetLockedBinary" || tc.name == "GetLockedByBinary" { input = []byte("nec.lthn.") } item, ok := tc.fn(input) if !ok { t.Fatalf("%s should resolve canonicalized locked names", tc.name) } if item.Name != "nec" { t.Fatalf("%s returned %q, want %q", tc.name, item.Name, "nec") } } } type packageTestCoinView struct { coins map[primitives.Outpoint]primitives.Output } func (v packageTestCoinView) GetOutput(prevout primitives.Outpoint) (primitives.Output, bool) { coin, ok := v.coins[prevout] return coin, ok } func TestPackageVerifyCovenants(t *testing.T) { hash, err := covenant.HashString("example-name") if err != nil { t.Fatalf("HashString returned error: %v", err) } var prevHash primitives.Hash prevHash[0] = 3 var finalHash primitives.Hash finalHash[0] = 4 committedAddress := append([]byte(nil), finalHash[:]...) outputAddress := append([]byte(nil), finalHash[:]...) transferCovenant := primitives.Covenant{ Type: uint8(covenant.TypeTransfer), Items: [][]byte{ hash[:], make([]byte, 4), []byte{0}, committedAddress, }, } binary.LittleEndian.PutUint32(transferCovenant.Items[1], 100) finalizeCovenant := primitives.Covenant{ Type: uint8(covenant.TypeFinalize), Items: [][]byte{ hash[:], make([]byte, 4), []byte("example-name"), []byte{0}, make([]byte, 4), make([]byte, 4), make([]byte, 32), }, } binary.LittleEndian.PutUint32(finalizeCovenant.Items[1], 100) binary.LittleEndian.PutUint32(finalizeCovenant.Items[4], 1) binary.LittleEndian.PutUint32(finalizeCovenant.Items[5], 2) tx := primitives.Transaction{ Inputs: []primitives.Input{ {Prevout: primitives.Outpoint{TxHash: prevHash, Index: 0}}, }, Outputs: []primitives.Output{ { Value: 1000, Address: primitives.Address{ Version: 0, Hash: outputAddress, }, Covenant: finalizeCovenant, }, }, } view := packageTestCoinView{ coins: map[primitives.Outpoint]primitives.Output{ primitives.Outpoint{TxHash: prevHash, Index: 0}: primitives.Output{ Value: 1000, Address: primitives.Address{ Version: 0, Hash: []byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, }, Covenant: transferCovenant, }, }, } if got := VerifyCovenants(tx, view, 100, covenant.Network{}); got != 0 { t.Fatalf("VerifyCovenants returned %d, want 0", got) } tx.Outputs[0].Address.Hash[0] ^= 1 if got := GetVerifyCovenants(tx, view, 100, covenant.Network{}); got != -1 { t.Fatalf("GetVerifyCovenants returned %d for an invalid finalize address, want -1", got) } } func TestPackageTypeConstants(t *testing.T) { cases := []struct { name string got covenant.CovenantType want covenant.CovenantType }{ {name: "TypeNone", got: TypeNone, want: covenant.TypeNone}, {name: "TypeClaim", got: TypeClaim, want: covenant.TypeClaim}, {name: "TypeOpen", got: TypeOpen, want: covenant.TypeOpen}, {name: "TypeBid", got: TypeBid, want: covenant.TypeBid}, {name: "TypeReveal", got: TypeReveal, want: covenant.TypeReveal}, {name: "TypeRedeem", got: TypeRedeem, want: covenant.TypeRedeem}, {name: "TypeRegister", got: TypeRegister, want: covenant.TypeRegister}, {name: "TypeUpdate", got: TypeUpdate, want: covenant.TypeUpdate}, {name: "TypeRenew", got: TypeRenew, want: covenant.TypeRenew}, {name: "TypeTransfer", got: TypeTransfer, want: covenant.TypeTransfer}, {name: "TypeFinalize", got: TypeFinalize, want: covenant.TypeFinalize}, {name: "TypeRevoke", got: TypeRevoke, want: covenant.TypeRevoke}, } for _, tc := range cases { if tc.got != tc.want { t.Fatalf("%s = %d, want %d", tc.name, tc.got, tc.want) } } if _, ok := Blacklist["test"]; !ok { t.Fatal("Blacklist should expose the covenant verifier blacklist") } if MaxNameSize != 63 { t.Fatalf("MaxNameSize = %d, want 63", MaxNameSize) } if MaxResourceSize != 512 { t.Fatalf("MaxResourceSize = %d, want 512", MaxResourceSize) } if VerifyCovenantsNone != 0 { t.Fatalf("VerifyCovenantsNone = %d, want 0", VerifyCovenantsNone) } if VerifyCovenantsHardened != 1<<0 { t.Fatalf("VerifyCovenantsHardened = %d, want %d", VerifyCovenantsHardened, 1<<0) } if VerifyCovenantsLockup != 1<<1 { t.Fatalf("VerifyCovenantsLockup = %d, want %d", VerifyCovenantsLockup, 1<<1) } if MandatoryVerifyCovenantFlags != VerifyCovenantsNone { t.Fatalf("MandatoryVerifyCovenantFlags = %d, want %d", MandatoryVerifyCovenantFlags, VerifyCovenantsNone) } if MaxCovenantSize != 585 { t.Fatalf("MaxCovenantSize = %d, want 585", MaxCovenantSize) } if CovenantMaxSize != MaxCovenantSize { t.Fatalf("CovenantMaxSize = %d, want %d", CovenantMaxSize, MaxCovenantSize) } } func TestPackagePrimitiveAliases(t *testing.T) { hash := primitives.Hash{1, 2, 3} address := Address{Version: 1, Hash: hash[:]} prevout := NewOutpoint() prevout.Index = 7 var ( _ = primitives.Address(address) _ = primitives.Outpoint(prevout) _ = OutpointJSON{Hash: prevout.GetJSON().Hash, Index: prevout.Index} _ = Input{Prevout: prevout} _ = Output{Address: address} _ = Transaction{Inputs: []Input{{Prevout: prevout}}} _ = BlockHeader{} _ = Covenant{} _ = CovenantJSON{} _ = NameStateJSON{} ) if got, want := NewOutpoint(), primitives.NewOutpoint(); got != want { t.Fatalf("NewOutpoint() = %+v, want %+v", got, want) } if got, want := GetNewOutpoint(), primitives.NewOutpoint(); got != want { t.Fatalf("GetNewOutpoint() = %+v, want %+v", got, want) } service := NewService(nil) if got, want := service.NewOutpoint(), primitives.NewOutpoint(); got != want { t.Fatalf("Service.NewOutpoint() = %+v, want %+v", got, want) } if got, want := service.GetNewOutpoint(), primitives.NewOutpoint(); got != want { t.Fatalf("Service.GetNewOutpoint() = %+v, want %+v", got, want) } } func TestPackageCovenantTypePredicates(t *testing.T) { if !IsName(TypeOpen) || !GetIsName(TypeOpen) { t.Fatal("IsName should report open covenants") } if IsName(TypeNone) || GetIsName(TypeNone) { t.Fatal("IsName should reject non-name covenants") } if !IsKnown(TypeBid) || !GetIsKnown(TypeBid) { t.Fatal("IsKnown should report recognized covenants") } if IsKnown(covenant.CovenantType(99)) || GetIsKnown(covenant.CovenantType(99)) { t.Fatal("IsKnown should reject unknown covenants") } if !IsLinked(TypeReveal) || !GetIsLinked(TypeReveal) { t.Fatal("IsLinked should report linked covenants") } if IsLinked(TypeOpen) || GetIsLinked(TypeOpen) { t.Fatal("IsLinked should reject unlinked covenants") } } func TestPackageCatalogAliases(t *testing.T) { reservedCatalog := ReservedCatalog() lockedCatalog := LockedCatalog() if reservedCatalog == nil { t.Fatal("ReservedCatalog should return a catalog") } if lockedCatalog == nil { t.Fatal("LockedCatalog should return a catalog") } if Reserved != reservedCatalog { t.Fatal("Reserved should alias ReservedCatalog") } if Locked != lockedCatalog { t.Fatal("Locked should alias LockedCatalog") } if GetReservedCatalog() != reservedCatalog { t.Fatal("GetReservedCatalog should alias ReservedCatalog") } if GetLockedCatalog() != lockedCatalog { t.Fatal("GetLockedCatalog should alias LockedCatalog") } if DefaultReservedCatalog() != reservedCatalog { t.Fatal("DefaultReservedCatalog should match ReservedCatalog") } if DefaultLockedCatalog() != lockedCatalog { t.Fatal("DefaultLockedCatalog should match LockedCatalog") } if GetDefaultReservedCatalog() != reservedCatalog { t.Fatal("GetDefaultReservedCatalog should match ReservedCatalog") } if GetDefaultLockedCatalog() != lockedCatalog { t.Fatal("GetDefaultLockedCatalog should match LockedCatalog") } if ReservedSize() != reservedCatalog.Size() || GetReservedSize() != reservedCatalog.Size() { t.Fatal("ReservedSize aliases should match the catalog size") } if LockedSize() != lockedCatalog.Size() || GetLockedSize() != lockedCatalog.Size() { t.Fatal("LockedSize aliases should match the catalog size") } if ReservedPrefixSize() != reservedCatalog.PrefixSize() || GetReservedPrefixSize() != reservedCatalog.PrefixSize() { t.Fatal("ReservedPrefixSize aliases should match the catalog prefix") } if NameValue() != reservedCatalog.NameValue() || GetNameValue() != reservedCatalog.NameValue() { t.Fatal("NameValue aliases should match the catalog base value") } if RootValue() != reservedCatalog.RootValue() || GetRootValue() != reservedCatalog.RootValue() { t.Fatal("RootValue aliases should match the catalog root value") } if TopValue() != reservedCatalog.TopValue() || GetTopValue() != reservedCatalog.TopValue() { t.Fatal("TopValue aliases should match the catalog top value") } if PrefixSize() != reservedCatalog.PrefixSize() || GetPrefixSize() != reservedCatalog.PrefixSize() { t.Fatal("PrefixSize aliases should match the reserved catalog prefix") } if LockedPrefixSize() != lockedCatalog.PrefixSize() || GetLockedPrefixSize() != lockedCatalog.PrefixSize() { t.Fatal("LockedPrefixSize aliases should match the catalog prefix") } if len(ReservedEntries()) == 0 || len(GetReservedEntries()) == 0 { t.Fatal("ReservedEntries aliases should expose catalog entries") } if len(ReservedKeys()) == 0 || len(GetReservedKeys()) == 0 { t.Fatal("ReservedKeys aliases should expose catalog keys") } if len(ReservedValues()) == 0 || len(GetReservedValues()) == 0 { t.Fatal("ReservedValues aliases should expose catalog values") } if len(LockedEntries()) == 0 || len(GetLockedEntries()) == 0 { t.Fatal("LockedEntries aliases should expose catalog entries") } if len(LockedKeys()) == 0 || len(GetLockedKeys()) == 0 { t.Fatal("LockedKeys aliases should expose catalog keys") } if len(LockedValues()) == 0 || len(GetLockedValues()) == 0 { t.Fatal("LockedValues aliases should expose catalog values") } if _, ok := GetReserved("RESERVED.lthn"); !ok { t.Fatal("GetReserved should find the reserved reference entry") } if !HasReserved("reserved.lthn") || !HasReservedByName("reserved") || !HasReservedName("reserved") { t.Fatal("reserved lookup aliases should report reserved names") } if _, ok := GetReservedByString("RESERVED"); !ok { t.Fatal("GetReservedByString should find the reserved catalog label") } if _, ok := GetReservedByBinary([]byte("reserved")); !ok { t.Fatal("GetReservedByBinary should find the reserved catalog label") } if _, ok := GetLocked("NEC.lthn"); !ok { t.Fatal("GetLocked should find the locked reference entry") } if !HasLocked("nec.lthn") || !HasLockedByName("nec") || !HasLockedName("nec") { t.Fatal("locked lookup aliases should report locked names") } if _, ok := GetLockedByString("NEC"); !ok { t.Fatal("GetLockedByString should find the locked catalog label") } if _, ok := GetLockedByBinary([]byte("nec")); !ok { t.Fatal("GetLockedByBinary should find the locked catalog label") } if _, ok := GetReservedByHash(reservedCatalog.Keys()[0]); !ok { t.Fatal("GetReservedByHash should return a reserved entry") } if !HasReservedHash(reservedCatalog.Keys()[0]) || !HasReservedByHash(reservedCatalog.Keys()[0]) { t.Fatal("HasReservedHash aliases should report reserved entries") } if _, ok := GetLockedByHash(lockedCatalog.Keys()[0]); !ok { t.Fatal("GetLockedByHash should return a locked entry") } if !HasLockedHash(lockedCatalog.Keys()[0]) || !HasLockedByHash(lockedCatalog.Keys()[0]) { t.Fatal("HasLockedHash aliases should report locked entries") } }