1576 lines
48 KiB
Go
1576 lines
48 KiB
Go
// 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 DefaultLockedCatalog() != covenant.DefaultLockedCatalog() {
|
|
t.Fatal("DefaultLockedCatalog should return the canonical locked catalog")
|
|
}
|
|
|
|
if DefaultLockedCatalog() == LockedCatalog() {
|
|
t.Fatal("DefaultLockedCatalog should ignore 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{}
|
|
_ = 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 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")
|
|
}
|
|
}
|