From a45d60bb2103668400ebac51e65c65eb5d1147e5 Mon Sep 17 00:00:00 2001 From: Damian Date: Thu, 27 Nov 2025 23:52:45 +0100 Subject: [PATCH 1/4] feat: validatie discoverByIdentityKeyArgs --- .../validate_discover_by_identity_key_args.go | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 pkg/internal/validate/validate_discover_by_identity_key_args.go diff --git a/pkg/internal/validate/validate_discover_by_identity_key_args.go b/pkg/internal/validate/validate_discover_by_identity_key_args.go new file mode 100644 index 00000000..e26c66fd --- /dev/null +++ b/pkg/internal/validate/validate_discover_by_identity_key_args.go @@ -0,0 +1,35 @@ +package validate + +import ( + "fmt" + + sdk "github.com/bsv-blockchain/go-sdk/wallet" + "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wdk/primitives" +) + +// DiscoverByIdentityKeyArgs validates arguments for DiscoverByIdentityKey() +func DiscoverByIdentityKeyArgs(args sdk.DiscoverByIdentityKeyArgs) error { + if args.Limit != nil { + if *args.Limit < MinPaginationLimit { + return fmt.Errorf("limit must be greater than 0") + } + if *args.Limit > MaxPaginationLimit { + return fmt.Errorf("limit exceeds max allowed value of %d", MaxPaginationLimit) + } + } + + if args.Offset != nil { + if *args.Offset > MaxPaginationOffset { + return fmt.Errorf("offset is too large") + } + } + + if args.IdentityKey != nil { + hex := primitives.PubKeyHex(args.IdentityKey.ToDERHex()) + if err := hex.Validate(); err != nil { + return fmt.Errorf("invalid verifier: failed validation check") + } + } + + return nil +} From 31ec5feba3f1629a95f693c32fcf0f1f1759e148 Mon Sep 17 00:00:00 2001 From: Damian Date: Thu, 27 Nov 2025 23:56:12 +0100 Subject: [PATCH 2/4] feat: add wallet settings menager --- .../internal/wallet_opts/wallet_opts.go | 2 + .../wallet_settings_manager.go | 145 ++++++++++++++++++ pkg/wallet/wallet.go | 62 +++++++- 3 files changed, 202 insertions(+), 7 deletions(-) create mode 100644 pkg/wallet/internal/wallet_settings_manager/wallet_settings_manager.go diff --git a/pkg/wallet/internal/wallet_opts/wallet_opts.go b/pkg/wallet/internal/wallet_opts/wallet_opts.go index d9cf2694..419945ea 100644 --- a/pkg/wallet/internal/wallet_opts/wallet_opts.go +++ b/pkg/wallet/internal/wallet_opts/wallet_opts.go @@ -8,6 +8,7 @@ import ( sdk "github.com/bsv-blockchain/go-sdk/wallet" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/services" + wallet_settings_manager "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/internal/wallet_settings_manager" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/pending" ) @@ -17,6 +18,7 @@ type Opts struct { Logger *slog.Logger PendingSignActionsRepo pending.SignActionsRepository Client *http.Client + WalletSettingsManager *wallet_settings_manager.WalletSettingsManager } type Flags struct { diff --git a/pkg/wallet/internal/wallet_settings_manager/wallet_settings_manager.go b/pkg/wallet/internal/wallet_settings_manager/wallet_settings_manager.go new file mode 100644 index 00000000..5e889955 --- /dev/null +++ b/pkg/wallet/internal/wallet_settings_manager/wallet_settings_manager.go @@ -0,0 +1,145 @@ +package wallet_settings_manager + +import ( + "sync" + "time" + + "github.com/bsv-blockchain/go-wallet-toolbox/pkg/defs" + "github.com/go-softwarelab/common/pkg/to" +) + +// Default certifiers for mainnet +var defaultCertifiers = []Certifier{ + { + Name: "Metanet Trust Services", + Description: "Registry for protocols, baskets, and certificates types", + IconURL: to.Ptr("https://bsvblockchain.org/favicon.ico"), + IdentityKey: "03daf815fe38f83da0ad83b5bedc520aa488aef5cbc93a93c67a7fe60406cbffe8", + Trust: 4, + }, + { + Name: "SocialCert", + Description: "Certifies social media handles, phone numbers and emails", + IconURL: to.Ptr("https://socialcert.net/favicon.ico"), + IdentityKey: "02cf6cdf466951d8dfc9e7c9367511d0007ed6fba35ed42d425cc412fd6cfd4a17", + Trust: 3, + }, +} + +// Testnet identity key mappings +var testnetIdentityKeys = map[string]string{ + "Babbage Trust Services": "03d0b36b5c98b000ec9ffed9a2cf005e279244edf6a19cf90545cdebe873162761", + "IdentiCert": "036dc48522aba1705afbb43df3c04dbd1da373b6154341a875bceaa2a3e7f21528", + "SocialCert": "02cf6cdf466951d8dfc9e7c9367511d0007ed6fba35ed42d425cc412fd6cfd4a17", +} + +// TrustSettingsCache is a struct for the single trust settings cache +type TrustSettingsCache struct { + ExpiresAt time.Time + TrustSettings *TrustSettings +} + +// TrustSettings contains the trust level and list of trusted certifiers +type TrustSettings struct { + TrustLevel int + TrustedCertifiers []Certifier +} + +// Certifier represents a trusted certificate authority +type Certifier struct { + Name string + Description string + IdentityKey string + Trust int + IconURL *string + BaseURL *string +} + +// WalletTheme represents theme settings +type WalletTheme struct { + Mode string +} + +// WalletSettings is the complete settings structure for wallet +type WalletSettings struct { + TrustSettings *TrustSettings + Theme *WalletTheme + Currency *string + PermissionMode *string +} + +// WalletSettingsManager manages wallet settings +type WalletSettingsManager struct { + settings *WalletSettings + mu sync.RWMutex +} + +func NewWalletSettingsManager(settings *WalletSettings) *WalletSettingsManager { + return &WalletSettingsManager{ + settings: settings, + } +} + +// Get returns the current wallet settings +func (wsm *WalletSettingsManager) Get() *WalletSettings { + wsm.mu.RLock() + defer wsm.mu.RUnlock() + return wsm.settings +} + +// Set assigns the current wallet settings to new settings +func (wsm *WalletSettingsManager) Set(settings *WalletSettings) { + wsm.mu.Lock() + defer wsm.mu.Unlock() + wsm.settings = settings +} + +func DefaultManager(chain defs.BSVNetwork) *WalletSettingsManager { + var trustedCertifiers []Certifier + + switch chain { + case defs.NetworkTestnet: + trustedCertifiers = GetTestnetDefaultCertifiers() + case defs.NetworkMainnet: + default: + trustedCertifiers = GetDefaultCertifiers() + } + + settings := &WalletSettings{ + TrustSettings: &TrustSettings{ + TrustLevel: 2, + TrustedCertifiers: trustedCertifiers, + }, + Theme: &WalletTheme{ + Mode: "dark", + }, + PermissionMode: to.Ptr("simple"), + } + + return &WalletSettingsManager{ + settings: settings, + } +} + +// GetTestnetDefaultCertifiers returns testnet settings with mapped identity keys +func GetTestnetDefaultCertifiers() []Certifier { + certifiers := make([]Certifier, len(defaultCertifiers)) + + for i, cert := range defaultCertifiers { + certifiers[i] = cert + // Use testnet key if available, otherwise keep the default + if testnetKey, exists := testnetIdentityKeys[cert.Name]; exists { + certifiers[i].IdentityKey = testnetKey + } + } + + return certifiers +} + +// GetDefaultCertifiers returns mainnet settings +func GetDefaultCertifiers() []Certifier { + certifiers := make([]Certifier, len(defaultCertifiers)) + copy(certifiers, defaultCertifiers) + + return certifiers +} diff --git a/pkg/wallet/wallet.go b/pkg/wallet/wallet.go index 1a854136..eb598a98 100644 --- a/pkg/wallet/wallet.go +++ b/pkg/wallet/wallet.go @@ -25,6 +25,7 @@ import ( "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/internal/mapping" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/internal/utils" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/internal/wallet_opts" + wallet_settings_manager "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/internal/wallet_settings_manager" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/pending" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wdk" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wdk/primitives" @@ -46,8 +47,17 @@ func (wc walletCleanupFunc) Add(next func()) walletCleanupFunc { } } +// CacheEntry is a struct for the map-based overlay cache entries +type CacheEntry struct { + ExpiresAt time.Time + value any +} + // Wallet is an implementation of the BRC-100 wallet interface. type Wallet struct { + trustSettingsCache *wallet_settings_manager.TrustSettingsCache + overlayCache map[string]*CacheEntry + settingsManager *wallet_settings_manager.WalletSettingsManager proto *sdk.ProtoWallet storage wdk.WalletStorage keyDeriver *sdk.KeyDeriver @@ -114,6 +124,13 @@ func WithPendingSignActionsRepository(cache pending.SignActionsRepository) func( } } +// WithWalletSettingsManager sets the WalletSettingsManager for wallet settings +func WithWalletSettingsManager(settingsManager *wallet_settings_manager.WalletSettingsManager) func(*wallet_opts.Opts) { + return func(opts *wallet_opts.Opts) { + opts.WalletSettingsManager = settingsManager + } +} + // WithLogger sets the provided slog.Logger to the Logger field in wallet_opts.Opts if the logger is not nil. func WithLogger(logger *slog.Logger) func(*wallet_opts.Opts) { return func(opts *wallet_opts.Opts) { @@ -154,6 +171,7 @@ func NewWithStorageFactory[KeySource PrivateKeySource, ActiveStorageFactory Stor Services: nil, PendingSignActionsRepo: nil, Client: wallet_opts.DefaultClient(), + WalletSettingsManager: wallet_settings_manager.DefaultManager(chain), }, opts...) keyDeriver, err := toKeyDeriver(keySource) @@ -183,6 +201,9 @@ func NewWithStorageFactory[KeySource PrivateKeySource, ActiveStorageFactory Stor logger: logger, userParty: userParty, randomizer: randomizer.New(), + overlayCache: make(map[string]*CacheEntry), + trustSettingsCache: nil, + settingsManager: options.WalletSettingsManager, } w.auth = clients.New(w, clients.WithHttpClientTransport(options.Client.Transport)) @@ -755,6 +776,40 @@ func (w *Wallet) acquireDirectCertificate(ctx context.Context, args sdk.AcquireC return &cert, nil } +func (w *Wallet) DiscoverByIdentityKey(ctx context.Context, args sdk.DiscoverByIdentityKeyArgs, originator string) (*sdk.DiscoverCertificatesResult, error) { + w.logger.DebugContext(ctx, "DiscoverByIdentityKey call", slogx.String("originator", originator)) + + if err := validate.Originator(originator); err != nil { + return nil, fmt.Errorf("invalid originator: %w", err) + } + + if err := validate.DiscoverByIdentityKeyArgs(args); err != nil { + return nil, fmt.Errorf("failed to validate sdk.DiscoverByIdentityKeyArgs: %w", err) + } + + // trustSettings cache (2 minutes) + now := time.Now() + const TTL = 2 * time.Minute + trustSettings := w.getTrustSettings(now, TTL) + fmt.Println("trustSettings", trustSettings) + + return nil, nil +} + +func (w *Wallet) getTrustSettings(now time.Time, ttl time.Duration) *wallet_settings_manager.TrustSettings { + var trustSettings *wallet_settings_manager.TrustSettings + if w.trustSettingsCache != nil && w.trustSettingsCache.ExpiresAt.After(now) { + trustSettings = w.trustSettingsCache.TrustSettings + } else { + trustSettings = w.settingsManager.Get().TrustSettings + w.trustSettingsCache = &wallet_settings_manager.TrustSettingsCache{ + ExpiresAt: now.Add(ttl), + TrustSettings: trustSettings, + } + } + return trustSettings +} + // ListCertificates lists identity certificates belonging to the user, filtered by certifier(s) and type(s). func (w *Wallet) ListCertificates(ctx context.Context, args sdk.ListCertificatesArgs, originator string) (*sdk.ListCertificatesResult, error) { w.logger.DebugContext(ctx, "ListCertificates call", slogx.String("originator", originator)) @@ -920,13 +975,6 @@ func (w *Wallet) RelinquishCertificate(ctx context.Context, args sdk.RelinquishC return &sdk.RelinquishCertificateResult{Relinquished: true}, nil } -// DiscoverByIdentityKey discovers identity certificates, issued to a given identity key by a trusted entity. -func (w *Wallet) DiscoverByIdentityKey(ctx context.Context, args sdk.DiscoverByIdentityKeyArgs, originator string) (*sdk.DiscoverCertificatesResult, error) { - w.logger.DebugContext(ctx, "DiscoverByIdentityKey call", slogx.String("originator", originator)) - // TODO implement me - panic("implement me") -} - // DiscoverByAttributes discovers identity certificates belonging to other users, where the documents contain // specific attributes, issued by a trusted entity. func (w *Wallet) DiscoverByAttributes(ctx context.Context, args sdk.DiscoverByAttributesArgs, originator string) (*sdk.DiscoverCertificatesResult, error) { From 35ccea4fb233c4e02e1e3c0eca70234ddcfe507d Mon Sep 17 00:00:00 2001 From: Damian Date: Fri, 28 Nov 2025 15:36:43 +0100 Subject: [PATCH 3/4] feat: change overlaycache and trustsettings cache to be thread safe --- pkg/wallet/wallet.go | 97 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 20 deletions(-) diff --git a/pkg/wallet/wallet.go b/pkg/wallet/wallet.go index eb598a98..6a119cf1 100644 --- a/pkg/wallet/wallet.go +++ b/pkg/wallet/wallet.go @@ -3,11 +3,15 @@ package wallet import ( "context" "encoding/base64" + "encoding/json" "errors" "fmt" "log/slog" "math" "net/http" + "sort" + "sync" + "sync/atomic" "time" "github.com/bsv-blockchain/go-sdk/auth/certificates" @@ -47,16 +51,23 @@ func (wc walletCleanupFunc) Add(next func()) walletCleanupFunc { } } -// CacheEntry is a struct for the map-based overlay cache entries -type CacheEntry struct { +// cacheEntry is a struct for the map-based overlay cache entries +type cacheEntry struct { ExpiresAt time.Time - value any + Value any +} + +// cacheKey represents the struct that will be a key in overlayCache +type cacheKey struct { + Fn string `json:"fn"` + IdentityKey string `json:"identityKey"` + Certifiers []string `json:"certifiers"` } // Wallet is an implementation of the BRC-100 wallet interface. type Wallet struct { - trustSettingsCache *wallet_settings_manager.TrustSettingsCache - overlayCache map[string]*CacheEntry + trustSettingsCache atomic.Pointer[wallet_settings_manager.TrustSettingsCache] + overlayCache sync.Map settingsManager *wallet_settings_manager.WalletSettingsManager proto *sdk.ProtoWallet storage wdk.WalletStorage @@ -201,9 +212,8 @@ func NewWithStorageFactory[KeySource PrivateKeySource, ActiveStorageFactory Stor logger: logger, userParty: userParty, randomizer: randomizer.New(), - overlayCache: make(map[string]*CacheEntry), - trustSettingsCache: nil, settingsManager: options.WalletSettingsManager, + // trustSettingsCache and overlayCache are zero-valued (ready to use) } w.auth = clients.New(w, clients.WithHttpClientTransport(options.Client.Transport)) @@ -777,6 +787,9 @@ func (w *Wallet) acquireDirectCertificate(ctx context.Context, args sdk.AcquireC } func (w *Wallet) DiscoverByIdentityKey(ctx context.Context, args sdk.DiscoverByIdentityKeyArgs, originator string) (*sdk.DiscoverCertificatesResult, error) { + const TTL = 2 * time.Minute + now := time.Now() + w.logger.DebugContext(ctx, "DiscoverByIdentityKey call", slogx.String("originator", originator)) if err := validate.Originator(originator); err != nil { @@ -788,25 +801,69 @@ func (w *Wallet) DiscoverByIdentityKey(ctx context.Context, args sdk.DiscoverByI } // trustSettings cache (2 minutes) - now := time.Now() - const TTL = 2 * time.Minute trustSettings := w.getTrustSettings(now, TTL) - fmt.Println("trustSettings", trustSettings) + certifiers := make([]string, len(trustSettings.TrustedCertifiers)) + for i, c := range trustSettings.TrustedCertifiers { + certifiers[i] = c.IdentityKey + } + sort.Strings(certifiers) + + // queryOverlay cache (2 minutes) + cacheKey := cacheKey{ + Fn: "discoverByIdentityKey", + IdentityKey: args.IdentityKey.ToDERHex(), + Certifiers: certifiers, + } + keyBytes, err := json.Marshal(cacheKey) + if err != nil { + return nil, fmt.Errorf("failed to marshal cacheKey: %w", err) + } + cacheKeyStr := string(keyBytes) + + // Check cache + cached, ok := w.overlayCache.Load(cacheKeyStr) + if !ok || !cached.(*cacheEntry).ExpiresAt.After(now) { + // Cache miss or expired - query overlay + // TODO: implement queryOverlay + // value := queryOverlay({ identityKey: args.IdentityKey, certifiers }, w.lookupResolver) + var value any = nil + + // Store in cache + cached = &cacheEntry{ + Value: value, + ExpiresAt: now.Add(TTL), + } + w.overlayCache.Store(cacheKeyStr, cached) + } + + entry := cached.(*cacheEntry) + if entry.Value == nil { + return &sdk.DiscoverCertificatesResult{ + TotalCertificates: 0, + Certificates: []sdk.IdentityCertificate{}, + }, nil + } - return nil, nil + // TODO: implement transformVerifiableCertificatesWithTrust + // return transformVerifiableCertificatesWithTrust(trustSettings, entry.Value) + return &sdk.DiscoverCertificatesResult{ + TotalCertificates: 0, + Certificates: []sdk.IdentityCertificate{}, + }, nil } func (w *Wallet) getTrustSettings(now time.Time, ttl time.Duration) *wallet_settings_manager.TrustSettings { - var trustSettings *wallet_settings_manager.TrustSettings - if w.trustSettingsCache != nil && w.trustSettingsCache.ExpiresAt.After(now) { - trustSettings = w.trustSettingsCache.TrustSettings - } else { - trustSettings = w.settingsManager.Get().TrustSettings - w.trustSettingsCache = &wallet_settings_manager.TrustSettingsCache{ - ExpiresAt: now.Add(ttl), - TrustSettings: trustSettings, - } + cached := w.trustSettingsCache.Load() + if cached != nil && cached.ExpiresAt.After(now) { + return cached.TrustSettings } + + trustSettings := w.settingsManager.Get().TrustSettings + w.trustSettingsCache.Store(&wallet_settings_manager.TrustSettingsCache{ + ExpiresAt: now.Add(ttl), + TrustSettings: trustSettings, + }) + return trustSettings } From 3903c57f92c324ad8fa723abe7d8e9a6e811eec2 Mon Sep 17 00:00:00 2001 From: Damian Date: Mon, 1 Dec 2025 16:57:39 +0100 Subject: [PATCH 4/4] feat: map certificates response and add lookupresolver --- go.mod | 16 +-- go.sum | 32 ++--- ...ookup_answer_to_verifiable_certificates.go | 70 +++++++++++ .../mapping/mapping_overlay_network.go | 17 +++ .../mapping/mapping_verifiable_certificate.go | 57 +++++++++ ...ping_verifiable_certificates_with_trust.go | 115 ++++++++++++++++++ .../internal/wallet_opts/wallet_opts.go | 2 + .../wallet_settings_manager.go | 1 + pkg/wallet/wallet.go | 50 ++++++-- 9 files changed, 324 insertions(+), 36 deletions(-) create mode 100644 pkg/wallet/internal/mapping/mapping_lookup_answer_to_verifiable_certificates.go create mode 100644 pkg/wallet/internal/mapping/mapping_overlay_network.go create mode 100644 pkg/wallet/internal/mapping/mapping_verifiable_certificate.go create mode 100644 pkg/wallet/internal/mapping/mapping_verifiable_certificates_with_trust.go diff --git a/go.mod b/go.mod index d1fae113..76e6143b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.3 require ( github.com/bsv-blockchain/go-bsv-middleware v0.11.0 - github.com/bsv-blockchain/go-sdk v1.2.11 + github.com/bsv-blockchain/go-sdk v1.2.12 github.com/bsv-blockchain/universal-test-vectors v0.6.1 github.com/filecoin-project/go-jsonrpc v0.9.0 github.com/go-co-op/gocron-gorm-lock/v2 v2.1.0 @@ -58,13 +58,13 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.43.0 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.46.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect - golang.org/x/tools v0.37.0 // indirect + golang.org/x/crypto v0.44.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/tools v0.38.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gorm.io/hints v1.1.2 // indirect ) diff --git a/go.sum b/go.sum index af69e833..475143c0 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/bsv-blockchain/go-bsv-middleware v0.11.0 h1:IZPxqIUB7K8Zq5Ys8HfRwh55J6noqoV8GiQ3BmkYbSg= github.com/bsv-blockchain/go-bsv-middleware v0.11.0/go.mod h1:iUyYAvciHPNOj3vVgak6aUpJ90GRr3lG1iV+KnU9pbo= -github.com/bsv-blockchain/go-sdk v1.2.11 h1:SK8kDuDZNP3ubvx0AL0bR/I8tXWljJICyUsiF4y9ZkQ= -github.com/bsv-blockchain/go-sdk v1.2.11/go.mod h1:S+8iokWX2la9G4mzwHIeCvYkADRzcdfk1AprN0z5MDI= +github.com/bsv-blockchain/go-sdk v1.2.12 h1:t/50ONqCTgumJH82YbQ8iqdo30ezIACyuFgvyHbkX9A= +github.com/bsv-blockchain/go-sdk v1.2.12/go.mod h1:1FWCWH+x6xc1kH9r6tuyRQqUomfrLBOQfdPesJZK/1k= github.com/bsv-blockchain/universal-test-vectors v0.6.1 h1:6mRV8T4ug8456p/rufoDselui3eKY6kr9mRYx8e87Rw= github.com/bsv-blockchain/universal-test-vectors v0.6.1/go.mod h1:aNNGIH9aN/aCQ9vw0gTiQiOajkyBQIPJM9O6nHhhF5g= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -239,39 +239,39 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -279,8 +279,8 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= diff --git a/pkg/wallet/internal/mapping/mapping_lookup_answer_to_verifiable_certificates.go b/pkg/wallet/internal/mapping/mapping_lookup_answer_to_verifiable_certificates.go new file mode 100644 index 00000000..52b6841c --- /dev/null +++ b/pkg/wallet/internal/mapping/mapping_lookup_answer_to_verifiable_certificates.go @@ -0,0 +1,70 @@ +package mapping + +import ( + "context" + "encoding/json" + "log/slog" + + "github.com/bsv-blockchain/go-sdk/auth/certificates" + "github.com/bsv-blockchain/go-sdk/overlay/lookup" + "github.com/bsv-blockchain/go-sdk/transaction" + "github.com/bsv-blockchain/go-sdk/transaction/template/pushdrop" + "github.com/bsv-blockchain/go-sdk/wallet" + "github.com/bsv-blockchain/go-wallet-toolbox/pkg/internal/logging" +) + +func MapLookupAnswerToVerifiableCertificates(ctx context.Context, logger *slog.Logger, lookupAns *lookup.LookupAnswer) []certificates.VerifiableCertificate { + if lookupAns.Type != lookup.AnswerTypeOutputList { + return nil + } + + parsedCerts := make([]certificates.VerifiableCertificate, 0) + + // On any error we do nothing and continue with other outputs as in TS + for _, output := range lookupAns.Outputs { + tx, err := transaction.NewTransactionFromBEEF(output.Beef) + if err != nil { + logger.Error("failed to create tx from output beef", slog.String("beef", string(output.Beef)), logging.Error(err)) + continue + } + + // Decode the Identity token fields from the Bitcoin outputScript + decodedOutput := pushdrop.Decode(tx.Outputs[output.OutputIndex].LockingScript) + + // Parse out the certificate and relevant data + var verifiableCertificate certificates.VerifiableCertificate + err = json.Unmarshal(decodedOutput.Fields[0], &verifiableCertificate) + if err != nil { + logger.Error("failed to unmarshal decodedOutput field into a certificate: %w ", logging.Error(err)) + continue + } + + anyoneProtoWallet, err := wallet.NewCompletedProtoWallet(nil) + if err != nil { + logger.Error("failed to create anyone's proto wallet: %w ", logging.Error(err)) + continue + } + + decryptedFields, err := verifiableCertificate.DecryptFields(ctx, + anyoneProtoWallet, + false, + "") + if err != nil { + logger.Error("failed to decrypt verifiableCertificate fields: %w ", logging.Error(err)) + continue + } + + // Verify the certificate signature is correct + err = verifiableCertificate.Verify(ctx) + if err != nil { + logger.Error("failed to verify certificate's signature: %w ", logging.Error(err)) + continue + } + + verifiableCertificate.DecryptedFields = decryptedFields + + parsedCerts = append(parsedCerts, verifiableCertificate) + } + + return parsedCerts +} diff --git a/pkg/wallet/internal/mapping/mapping_overlay_network.go b/pkg/wallet/internal/mapping/mapping_overlay_network.go new file mode 100644 index 00000000..795d73ed --- /dev/null +++ b/pkg/wallet/internal/mapping/mapping_overlay_network.go @@ -0,0 +1,17 @@ +package mapping + +import ( + "github.com/bsv-blockchain/go-sdk/overlay" + "github.com/bsv-blockchain/go-wallet-toolbox/pkg/defs" +) + +func MapToOverlayNetwork(chain defs.BSVNetwork) overlay.Network { + switch chain { + case defs.NetworkMainnet: + return overlay.NetworkMainnet + case defs.NetworkTestnet: + return overlay.NetworkTestnet + default: + return overlay.NetworkTestnet + } +} diff --git a/pkg/wallet/internal/mapping/mapping_verifiable_certificate.go b/pkg/wallet/internal/mapping/mapping_verifiable_certificate.go new file mode 100644 index 00000000..5ea5c32e --- /dev/null +++ b/pkg/wallet/internal/mapping/mapping_verifiable_certificate.go @@ -0,0 +1,57 @@ +package mapping + +import ( + "encoding/base64" + "fmt" + + "github.com/bsv-blockchain/go-sdk/auth/certificates" + ec "github.com/bsv-blockchain/go-sdk/primitives/ec" + "github.com/bsv-blockchain/go-sdk/wallet" + "github.com/go-softwarelab/common/pkg/to" +) + +func MapVerifiableCertificateToCertificate(cert certificates.VerifiableCertificate) (wallet.Certificate, error) { + serialBytes, err := base64.StdEncoding.DecodeString(string(cert.SerialNumber)) + if err != nil { + return wallet.Certificate{}, fmt.Errorf("failed to decode certificate serial number: %w", err) + } + + var serial wallet.SerialNumber + if len(serialBytes) != len(serial) { + return wallet.Certificate{}, fmt.Errorf("serial bytes length: %d is not equal to wallet.SerialNumber bytes length: %d", len(serialBytes), len(serial)) + } + + copy(serial[:], serialBytes) + + certTypeBytes, err := base64.StdEncoding.DecodeString(string(cert.Type)) + if err != nil { + return wallet.Certificate{}, fmt.Errorf("failed to decode certificate type: %w", err) + } + + var certType wallet.CertificateType + if len(certType) != len(certTypeBytes) { + return wallet.Certificate{}, fmt.Errorf("certificate type bytes length: %d is not equal to sdk.CertificateType bytes length: %d", len(certTypeBytes), len(certType)) + } + + copy(certType[:], certTypeBytes) + + fields := make(map[string]string, len(cert.Fields)) + for k, v := range cert.Fields { + fields[to.String(k)] = to.String(v) + } + + signature, err := ec.ParseSignature(cert.Signature) + if err != nil { + return wallet.Certificate{}, fmt.Errorf("failed to parse signature: %w", err) + } + + return wallet.Certificate{ + Type: certType, + SerialNumber: serial, + Subject: to.Ptr(cert.Subject), + Certifier: to.Ptr(cert.Certifier), + RevocationOutpoint: cert.RevocationOutpoint, + Fields: fields, + Signature: signature, + }, nil +} diff --git a/pkg/wallet/internal/mapping/mapping_verifiable_certificates_with_trust.go b/pkg/wallet/internal/mapping/mapping_verifiable_certificates_with_trust.go new file mode 100644 index 00000000..53e5226e --- /dev/null +++ b/pkg/wallet/internal/mapping/mapping_verifiable_certificates_with_trust.go @@ -0,0 +1,115 @@ +package mapping + +import ( + "log/slog" + "slices" + + "github.com/bsv-blockchain/go-sdk/auth/certificates" + "github.com/bsv-blockchain/go-sdk/wallet" + "github.com/bsv-blockchain/go-wallet-toolbox/pkg/internal/logging" + "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/internal/wallet_settings_manager" + "github.com/go-softwarelab/common/pkg/to" +) + +// identityGroup is a helper type used for grouping certificates by subject +type identityGroup struct { + TotalTrust int + Members []wallet.IdentityCertificate +} + +// MapVerifiableCertificatesWithTrust transforms an array of VerifiableCertificate +// instances according to the trust settings. +// Only certificates whose grouped total trust meets the threshold are returned, +// and each certificate is augmented with a certifierInfo property. +func MapVerifiableCertificatesWithTrust(logger *slog.Logger, trustSettings *wallet_settings_manager.TrustSettings, certificates []certificates.VerifiableCertificate) (*wallet.DiscoverCertificatesResult, error) { + if logger == nil { + logger = slog.Default() + } + // Group certificates by subject while accumulating trust. + identityGroups := make(map[string]identityGroup) + // Cache certifier lookups. + certifierCache := make(map[string]wallet_settings_manager.Certifier) + + for _, cert := range certificates { + certifierIdentityKey := cert.Certifier.ToDERHex() + subjectIdentityKey := cert.Subject.ToDERHex() + + // Lookup and cache certifier details from trustSettings + trustedCertifier, ok := certifierCache[certifierIdentityKey] + if !ok { + var found bool + trustedCertifier, found = findCertifier(trustSettings.TrustedCertifiers, certifierIdentityKey) + if !found { + // Skip this certificate if its certifier is not trusted. + continue + } + certifierCache[certifierIdentityKey] = trustedCertifier + } + + // Create the IdentityCertifier object that we want to attach. + certifierInfo := wallet.IdentityCertifier{ + Name: trustedCertifier.Name, + IconUrl: to.Value(trustedCertifier.IconURL), + Description: trustedCertifier.Description, + Trust: uint8(trustedCertifier.Trust), + } + + // Create an extended certificate that includes certifierInfo. + // Note: We use object spread to copy over all properties from the original certificate. + revealedKeyring := make(map[string]string, len(cert.Keyring)) + for k, v := range cert.Keyring { + revealedKeyring[to.String(k)] = to.String(v) + } + + mappedCert, err := MapVerifiableCertificateToCertificate(cert) + if err != nil { + // continue on failed certificate mapping but log the error + // matches TS version where it doesn't stop on any map failure + logger.Error("failed to map verifiable certificate to wallet.Certificate: %w", logging.Error(err)) + continue + } + + extendedCert := wallet.IdentityCertificate{ + Certificate: mappedCert, + DecryptedFields: cert.DecryptedFields, + PubliclyRevealedKeyring: revealedKeyring, + CertifierInfo: certifierInfo, + } + + // Group certificates by subject + if _, ok := identityGroups[subjectIdentityKey]; !ok { + identityGroups[subjectIdentityKey] = identityGroup{} + } + group := identityGroups[subjectIdentityKey] + group.TotalTrust += int(certifierInfo.Trust) + group.Members = append(group.Members, extendedCert) + identityGroups[subjectIdentityKey] = group + } + + results := make([]wallet.IdentityCertificate, 0) + // Filter out groups that do not meet the trust threshold and flatten the results. + for _, group := range identityGroups { + if group.TotalTrust >= trustSettings.TrustLevel { + results = append(results, group.Members...) + } + } + + slices.SortFunc(results, func(a, b wallet.IdentityCertificate) int { + return int(b.CertifierInfo.Trust) - int(a.CertifierInfo.Trust) + }) + + return &wallet.DiscoverCertificatesResult{ + TotalCertificates: uint32(len(results)), + Certificates: results, + }, nil +} + +func findCertifier(certifiers []wallet_settings_manager.Certifier, identityKey string) (wallet_settings_manager.Certifier, bool) { + for _, certifier := range certifiers { + if certifier.IdentityKey == identityKey { + return certifier, true + } + } + + return wallet_settings_manager.Certifier{}, false +} diff --git a/pkg/wallet/internal/wallet_opts/wallet_opts.go b/pkg/wallet/internal/wallet_opts/wallet_opts.go index 419945ea..365fdf3d 100644 --- a/pkg/wallet/internal/wallet_opts/wallet_opts.go +++ b/pkg/wallet/internal/wallet_opts/wallet_opts.go @@ -6,6 +6,7 @@ import ( "net/http" "time" + "github.com/bsv-blockchain/go-sdk/overlay/lookup" sdk "github.com/bsv-blockchain/go-sdk/wallet" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/services" wallet_settings_manager "github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet/internal/wallet_settings_manager" @@ -19,6 +20,7 @@ type Opts struct { PendingSignActionsRepo pending.SignActionsRepository Client *http.Client WalletSettingsManager *wallet_settings_manager.WalletSettingsManager + LookupResolver *lookup.LookupResolver } type Flags struct { diff --git a/pkg/wallet/internal/wallet_settings_manager/wallet_settings_manager.go b/pkg/wallet/internal/wallet_settings_manager/wallet_settings_manager.go index 5e889955..8c42a7c0 100644 --- a/pkg/wallet/internal/wallet_settings_manager/wallet_settings_manager.go +++ b/pkg/wallet/internal/wallet_settings_manager/wallet_settings_manager.go @@ -101,6 +101,7 @@ func DefaultManager(chain defs.BSVNetwork) *WalletSettingsManager { case defs.NetworkTestnet: trustedCertifiers = GetTestnetDefaultCertifiers() case defs.NetworkMainnet: + trustedCertifiers = GetDefaultCertifiers() default: trustedCertifiers = GetDefaultCertifiers() } diff --git a/pkg/wallet/wallet.go b/pkg/wallet/wallet.go index 6a119cf1..c225598e 100644 --- a/pkg/wallet/wallet.go +++ b/pkg/wallet/wallet.go @@ -16,6 +16,7 @@ import ( "github.com/bsv-blockchain/go-sdk/auth/certificates" clients "github.com/bsv-blockchain/go-sdk/auth/clients/authhttp" + "github.com/bsv-blockchain/go-sdk/overlay/lookup" ec "github.com/bsv-blockchain/go-sdk/primitives/ec" sdk "github.com/bsv-blockchain/go-sdk/wallet" "github.com/bsv-blockchain/go-wallet-toolbox/pkg/defs" @@ -54,7 +55,7 @@ func (wc walletCleanupFunc) Add(next func()) walletCleanupFunc { // cacheEntry is a struct for the map-based overlay cache entries type cacheEntry struct { ExpiresAt time.Time - Value any + Value []certificates.VerifiableCertificate } // cacheKey represents the struct that will be a key in overlayCache @@ -64,11 +65,18 @@ type cacheKey struct { Certifiers []string `json:"certifiers"` } +// identityQuery is a struct representing query to the lookupResolver. +type identityQuery struct { + IdentityKey string `json:"identityKey"` + Certifiers []string `json:"certifiers"` +} + // Wallet is an implementation of the BRC-100 wallet interface. type Wallet struct { trustSettingsCache atomic.Pointer[wallet_settings_manager.TrustSettingsCache] overlayCache sync.Map settingsManager *wallet_settings_manager.WalletSettingsManager + lookupResolver *lookup.LookupResolver proto *sdk.ProtoWallet storage wdk.WalletStorage keyDeriver *sdk.KeyDeriver @@ -107,6 +115,13 @@ func WithAuthHTTPClient(client *http.Client) func(*wallet_opts.Opts) { } } +// WithLookupResolver configures a lookup resolver for the wallet. +func WithLookupResolver(lookupResolver *lookup.LookupResolver) func(*wallet_opts.Opts) { + return func(o *wallet_opts.Opts) { + o.LookupResolver = lookupResolver + } +} + // WithTrustSelf - default: `known` // controls behavior of input BEEF validation. // If "known", input transactions may omit supporting validity proof data for all TXIDs known to this wallet. @@ -183,6 +198,9 @@ func NewWithStorageFactory[KeySource PrivateKeySource, ActiveStorageFactory Stor PendingSignActionsRepo: nil, Client: wallet_opts.DefaultClient(), WalletSettingsManager: wallet_settings_manager.DefaultManager(chain), + LookupResolver: lookup.NewLookupResolver(&lookup.LookupResolver{ + NetworkPreset: mapping.MapToOverlayNetwork(chain), + }), }, opts...) keyDeriver, err := toKeyDeriver(keySource) @@ -213,7 +231,6 @@ func NewWithStorageFactory[KeySource PrivateKeySource, ActiveStorageFactory Stor userParty: userParty, randomizer: randomizer.New(), settingsManager: options.WalletSettingsManager, - // trustSettingsCache and overlayCache are zero-valued (ready to use) } w.auth = clients.New(w, clients.WithHttpClientTransport(options.Client.Transport)) @@ -824,13 +841,27 @@ func (w *Wallet) DiscoverByIdentityKey(ctx context.Context, args sdk.DiscoverByI cached, ok := w.overlayCache.Load(cacheKeyStr) if !ok || !cached.(*cacheEntry).ExpiresAt.After(now) { // Cache miss or expired - query overlay - // TODO: implement queryOverlay - // value := queryOverlay({ identityKey: args.IdentityKey, certifiers }, w.lookupResolver) - var value any = nil + query, err := json.Marshal(identityQuery{ + IdentityKey: args.IdentityKey.ToDERHex(), + Certifiers: certifiers, + }) + if err != nil { + return nil, fmt.Errorf("failed to marshal overlay query: %w", err) + } + + lookupAnswer, err := w.lookupResolver.Query(ctx, &lookup.LookupQuestion{ + Service: "ls_identity", + Query: query, + }) + if err != nil { + return nil, fmt.Errorf("failed to query lookupResolver: %w", err) + } + + verifiableCertificates := mapping.MapLookupAnswerToVerifiableCertificates(ctx, w.logger, lookupAnswer) // Store in cache cached = &cacheEntry{ - Value: value, + Value: verifiableCertificates, ExpiresAt: now.Add(TTL), } w.overlayCache.Store(cacheKeyStr, cached) @@ -844,12 +875,7 @@ func (w *Wallet) DiscoverByIdentityKey(ctx context.Context, args sdk.DiscoverByI }, nil } - // TODO: implement transformVerifiableCertificatesWithTrust - // return transformVerifiableCertificatesWithTrust(trustSettings, entry.Value) - return &sdk.DiscoverCertificatesResult{ - TotalCertificates: 0, - Certificates: []sdk.IdentityCertificate{}, - }, nil + return mapping.MapVerifiableCertificatesWithTrust(w.logger, trustSettings, entry.Value) } func (w *Wallet) getTrustSettings(now time.Time, ttl time.Duration) *wallet_settings_manager.TrustSettings {