Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(BUX-395): add pike capabilitiy #77

Merged
merged 6 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion brfc_definintions.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const (
BRFCSFPBuildAction = "189e32d93d28" // more info: https://docs.moneybutton.com/docs/sfp/paymail-09-sfp-build.html
BRFCVerifyPublicKeyOwner = "a9f510c16bde" // more info: http://bsvalias.org/05-verify-public-key-owner.html
BRFCBeefTransaction = "5c55a7fdb7bb" // more info: https://bsv.brc.dev/payments/0070

BRFCPike = "8c4ed5ef8ace" // more info: TODO
wregulski marked this conversation as resolved.
Show resolved Hide resolved
)

// BRFCKnownSpecifications is a running list of all known BRFC specifications
Expand Down Expand Up @@ -196,6 +198,13 @@ const BRFCKnownSpecifications = `
"title": "Background Evaluation Extended Format Transaction",
"url": "https://bsv.brc.dev/payments/0070",
"version": "1.0.0"
}
},
{
"author": "Damian Orzepowski",
"id": "8c4ed5ef8ace",
"title": "PIKE",
"url": "TODO",
"version": "1.0.0"
}
]
`
8 changes: 8 additions & 0 deletions examples/server/run_server/demo_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,11 @@ func (d *demoServiceProvider) VerifyMerkleRoots(ctx context.Context, merkleProof
// Verify the Merkle roots
return nil
}

func (d *demoServiceProvider) AddContact(
ctx context.Context,
requesterPaymail string,
contact *paymail.PikeContactRequestPayload,
) error {
return nil
}
9 changes: 7 additions & 2 deletions examples/server/run_server/run_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package main

import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"

"github.com/gin-gonic/gin"

"github.com/bitcoin-sv/go-paymail/logging"

"github.com/bitcoin-sv/go-paymail/server"
Expand All @@ -19,9 +20,13 @@ func main() {
logger.Fatal().Msg(err.Error())
}

sl := server.PaymailServiceLocator{}
sl.RegisterPaymailService(new(demoServiceProvider))
sl.RegisterPikeService(new(demoServiceProvider))

// Custom server with lots of customizable goodies
config, err := server.NewConfig(
new(demoServiceProvider),
&sl,
server.WithBasicRoutes(),
server.WithDomain("localhost"),
server.WithDomain("another.com"),
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ require (
github.com/gin-gonic/gin v1.9.1
github.com/go-resty/resty/v2 v2.11.0
github.com/jarcoal/httpmock v1.3.1
github.com/julienschmidt/httprouter v1.3.0
github.com/libsv/go-bc v0.1.25
github.com/libsv/go-bk v0.1.6
github.com/libsv/go-bt/v2 v2.2.5
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInw
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
Expand Down
1 change: 1 addition & 0 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ type ClientInterface interface {
VerifyPubKey(verifyURL, alias, domain, pubKey string) (response *VerificationResponse, err error)
WithCustomHTTPClient(client *resty.Client) ClientInterface
WithCustomResolver(resolver interfaces.DNSResolver) ClientInterface
AddContactRequest(url, alias, domain string, request *PikeContactRequestPayload) (response *PikeContactRequestResponse, err error)
}
83 changes: 83 additions & 0 deletions pike.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package paymail

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
)

type PikeContactRequestResponse struct {
StandardResponse
}

type PikeContactRequestPayload struct {
FullName string `json:"fullName"`
Paymail string `json:"paymail"`
}

func (c *Client) AddContactRequest(url, alias, domain string, request *PikeContactRequestPayload) (*PikeContactRequestResponse, error) {

if err := c.validateUrlWithPaymail(url, alias, domain); err != nil {
return nil, err
}

if err := request.validate(); err != nil {
return nil, err
}

// Set the base url and path, assuming the url is from the prior GetCapabilities() request
// https://<host-discovery-target>/{alias}@{domain.tld}/id
reqURL := replaceAliasDomain(url, alias, domain)

response, err := c.postRequest(reqURL, request)
if err != nil {
return nil, err
}

if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusCreated {
if response.StatusCode == http.StatusNotFound {
return nil, errors.New("paymail address not found")
} else {
return nil, c.prepareServerErrorResponse(&response)
}
}

return &PikeContactRequestResponse{response}, nil
}

func (c *Client) validateUrlWithPaymail(url, alias, domain string) error {
if len(url) == 0 || !strings.HasPrefix(url, "https://") {
return fmt.Errorf("invalid url: %s", url)
} else if alias == "" {
return errors.New("missing alias")
} else if domain == "" {
return errors.New("missing domain")
}
return nil
}

func (c *Client) prepareServerErrorResponse(response *StandardResponse) error {
var details string

serverError := &ServerError{}
if err := json.Unmarshal(response.Body, serverError); err != nil || serverError.Message == "" {
details = fmt.Sprintf("body: %s", string(response.Body))
} else {
details = fmt.Sprintf("message: %s", serverError.Message)
}

return fmt.Errorf("bad response from paymail provider: code %d, %s", response.StatusCode, details)
}

func (r *PikeContactRequestPayload) validate() error {
if r.FullName == "" {
return errors.New("missing full name")
}
if r.Paymail == "" {
return errors.New("missing paymail address")
}

return ValidatePaymail(r.Paymail)
}
15 changes: 14 additions & 1 deletion server/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package server

import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"strings"

"github.com/gin-gonic/gin"

"github.com/bitcoin-sv/go-paymail"
)

Expand Down Expand Up @@ -79,6 +80,18 @@ func (c *Configuration) SetBeefCapabilities() {
)
}

func (c *Configuration) SetPikeCapabilities() {
_addCapabilities(c.callableCapabilities,
CallableCapabilitiesMap{
paymail.BRFCPike: CallableCapability{
Path: fmt.Sprintf("/pike/%s", PaymailAddressTemplate),
Method: http.MethodPost,
Handler: c.pikeNewContact,
},
},
)
}

func _addCapabilities[T any](base map[string]T, newCaps map[string]T) {
for key, val := range newCaps {
base[key] = val
Expand Down
11 changes: 9 additions & 2 deletions server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ type Configuration struct {
GenericCapabilitiesEnabled bool `json:"generic_capabilities_enabled"`
P2PCapabilitiesEnabled bool `json:"p2p_capabilities_enabled"`
BeefCapabilitiesEnabled bool `json:"beef_capabilities_enabled"`
PikeCapabilitiesEnabled bool `json:"pike_capabilities_enabled"`
ServiceName string `json:"service_name"`
Timeout time.Duration `json:"timeout"`
Logger *zerolog.Logger `json:"logger"`

// private
actions PaymailServiceProvider
pikeActions PikeServiceProvider
callableCapabilities CallableCapabilitiesMap
staticCapabilities StaticCapabilitiesMap
}
Expand Down Expand Up @@ -112,7 +114,8 @@ func (c *Configuration) AddDomain(domain string) (err error) {
}

// NewConfig will make a new server configuration
func NewConfig(serviceProvider PaymailServiceProvider, opts ...ConfigOps) (*Configuration, error) {
// The serviceProvider must have registered necessary services before calling them (e.g., PikeServiceProvider has to be registered if Pike capabilities are supported)
func NewConfig(serviceProvider *PaymailServiceLocator, opts ...ConfigOps) (*Configuration, error) {
chris-4chain marked this conversation as resolved.
Show resolved Hide resolved

// Check that a service provider is set
if serviceProvider == nil {
Expand All @@ -136,14 +139,18 @@ func NewConfig(serviceProvider PaymailServiceProvider, opts ...ConfigOps) (*Conf
if config.BeefCapabilitiesEnabled {
config.SetBeefCapabilities()
}
if config.PikeCapabilitiesEnabled {
config.SetPikeCapabilities()
config.pikeActions = serviceProvider.GetPikeService()
}

// Validate the configuration
if err := config.Validate(); err != nil {
return nil, err
}

// Set the service provider
config.actions = serviceProvider
config.actions = serviceProvider.GetPaymailService()

config.Logger.Debug().Msg("New config loaded")
return config, nil
Expand Down
8 changes: 8 additions & 0 deletions server/config_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func defaultConfigOptions() *Configuration {
GenericCapabilitiesEnabled: true,
P2PCapabilitiesEnabled: false,
BeefCapabilitiesEnabled: false,
PikeCapabilitiesEnabled: false,
ServiceName: paymail.DefaultServiceName,
Timeout: DefaultTimeout,
Logger: logging.GetDefaultLogger(),
Expand Down Expand Up @@ -58,6 +59,13 @@ func WithBeefCapabilities() ConfigOps {
}
}

// WithPikeCapabilities will load the PIKE capabilities
func WithPikeCapabilities() ConfigOps {
return func(c *Configuration) {
c.PikeCapabilitiesEnabled = true
}
}

// WithCapabilities will modify the capabilities
func WithCapabilities(customCapabilities map[string]any) ConfigOps {
return func(c *Configuration) {
Expand Down
Loading
Loading