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

Release candidate v1.1.0 #443

Merged
merged 10 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ data/

# Configuration files
.env.config

# VSCode
.vscode/

# Ignore config file
config.yaml
5 changes: 0 additions & 5 deletions .vscode/extensions.json

This file was deleted.

14 changes: 0 additions & 14 deletions .vscode/launch.json

This file was deleted.

23 changes: 0 additions & 23 deletions .vscode/settings.json

This file was deleted.

17 changes: 0 additions & 17 deletions .vscode/tasks.json

This file was deleted.

86 changes: 0 additions & 86 deletions actions/admin/block_headers.go

This file was deleted.

2 changes: 0 additions & 2 deletions actions/admin/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ func RegisterRoutes(router *apirouter.Router, appConfig *config.AppConfig, servi
router.HTTPRouter.GET("/"+config.APIVersion+"/admin/status", action.Request(router, require.Wrap(action.status)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/access-keys/search", action.Request(router, require.Wrap(action.accessKeysSearch)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/access-keys/count", action.Request(router, require.Wrap(action.accessKeysCount)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/block-headers/search", action.Request(router, require.Wrap(action.blockHeadersSearch)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/block-headers/count", action.Request(router, require.Wrap(action.blockHeadersCount)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/destinations/search", action.Request(router, require.Wrap(action.destinationsSearch)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/destinations/count", action.Request(router, require.Wrap(action.destinationsCount)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/paymail/get", action.Request(router, require.Wrap(action.paymailGetAddress)))
Expand Down
1 change: 0 additions & 1 deletion actions/destinations/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ func (a *Action) create(w http.ResponseWriter, req *http.Request, _ httprouter.P
xPub.RawXpub(),
uint32(0), // todo: use a constant? protect this?
scriptType,
true, // monitor this address as it was created by request of a user to share
opts...,
); err != nil {
apirouter.ReturnResponse(w, req, http.StatusUnprocessableEntity, err.Error())
Expand Down
45 changes: 39 additions & 6 deletions actions/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package actions

import (
"net/http"
"strings"
"time"

"github.com/BuxOrg/bux"
Expand All @@ -21,14 +22,14 @@ type Action struct {

// NewStack is used for registering routes
func NewStack(appConfig *config.AppConfig,
services *config.AppServices) (Action, *apirouter.InternalStack) {
services *config.AppServices,
) (Action, *apirouter.InternalStack) {
return Action{AppConfig: appConfig, Services: services}, apirouter.NewStack()
}

// RequireAuthentication checks and requires authentication for the related method
func (a *Action) RequireAuthentication(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {

// Check the authentication
var knownErr dictionary.ErrorMessage
if req, knownErr = CheckAuthentication(a.AppConfig, a.Services.Bux, req, false, true); knownErr.Code > 0 {
Expand All @@ -44,7 +45,6 @@ func (a *Action) RequireAuthentication(fn httprouter.Handle) httprouter.Handle {
// RequireBasicAuthentication checks and requires authentication for the related method, but does not require signing
func (a *Action) RequireBasicAuthentication(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {

// Check the authentication
var knownErr dictionary.ErrorMessage
if req, knownErr = CheckAuthentication(a.AppConfig, a.Services.Bux, req, false, false); knownErr.Code > 0 {
Expand All @@ -60,7 +60,6 @@ func (a *Action) RequireBasicAuthentication(fn httprouter.Handle) httprouter.Han
// RequireAdminAuthentication checks and requires ADMIN authentication for the related method
func (a *Action) RequireAdminAuthentication(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {

// Check the authentication
var knownErr dictionary.ErrorMessage
if req, knownErr = CheckAuthentication(a.AppConfig, a.Services.Bux, req, true, true); knownErr.Code > 0 {
Expand All @@ -73,15 +72,29 @@ func (a *Action) RequireAdminAuthentication(fn httprouter.Handle) httprouter.Han
}
}

// RequireCallbackAuthentication checks and requires callback Authorize Bearer token for the related method
func (a *Action) RequireCallbackAuthentication(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
var knownErr dictionary.ErrorMessage
if req, knownErr = VerifyCallbackToken(a.AppConfig, req); knownErr.Code > 0 {
ReturnErrorResponse(w, req, knownErr, "")
return
}

// Continue to next method
fn(w, req, p)
}
}

// Request will process the request in the router
func (a *Action) Request(_ *apirouter.Router, h httprouter.Handle) httprouter.Handle {
return Request(h, a)
}

// CheckAuthentication will check the authentication
func CheckAuthentication(appConfig *config.AppConfig, bux bux.ClientInterface, req *http.Request,
adminRequired bool, requireSigning bool) (*http.Request, dictionary.ErrorMessage) {

adminRequired bool, requireSigning bool,
) (*http.Request, dictionary.ErrorMessage) {
// Bad/Unknown scheme
if appConfig.Authentication.Scheme != config.AuthenticationSchemeXpub {
return req, dictionary.GetError(dictionary.ErrorAuthenticationScheme, appConfig.Authentication.Scheme)
Expand All @@ -102,6 +115,26 @@ func CheckAuthentication(appConfig *config.AppConfig, bux bux.ClientInterface, r
return req, dictionary.ErrorMessage{}
}

// VerifyCallbackToken verifies the callback token - if it's valid and matches the Bearer scheme.
func VerifyCallbackToken(appConfig *config.AppConfig, req *http.Request) (*http.Request, dictionary.ErrorMessage) {
const BearerSchema = "Bearer "
authHeader := req.Header.Get("Authorization")
if authHeader == "" {
return req, dictionary.GetError(dictionary.ErrorAuthenticationCallback, "missing auth header")
}

if !strings.HasPrefix(authHeader, BearerSchema) || len(authHeader) <= len(BearerSchema) {
return req, dictionary.GetError(dictionary.ErrorAuthenticationCallback, "invalid or missing bearer token")
}

providedToken := authHeader[len(BearerSchema):]
if providedToken != appConfig.Nodes.Callback.CallbackToken {
return req, dictionary.GetError(dictionary.ErrorAuthenticationCallback, "invalid authorization token")
}

return req, dictionary.ErrorMessage{}
}

// Request will write the request to the logs before and after calling the handler
func Request(h httprouter.Handle, a *Action) httprouter.Handle {
return parameters.MakeHTTPRouterParsedReq(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
Expand Down
44 changes: 44 additions & 0 deletions actions/transactions/broadcast_callback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package transactions

import (
"encoding/json"
"net/http"

"github.com/bitcoin-sv/go-broadcast-client/broadcast"
"github.com/julienschmidt/httprouter"
apirouter "github.com/mrz1836/go-api-router"
)

// broadcastCallback will handle a broadcastCallback call from the broadcast api
// Broadcast Callback godoc
// @Summary Broadcast Callback
// @Tags Transactions
// @Param transaction body broadcast.SubmittedTx true "transaction"
// @Success 200
// @Router /transaction/broadcast/callback [post]
// @Security callback-auth
func (a *Action) broadcastCallback(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
var resp *broadcast.SubmittedTx

err := json.NewDecoder(req.Body).Decode(&resp)
if err != nil {
apirouter.ReturnResponse(w, req, http.StatusExpectationFailed, err.Error())
return
}

defer func() {
if err = req.Body.Close(); err != nil {
a.Services.Logger.Err(err).Msg("failed to close request body")
}
}()

err = a.Services.Bux.UpdateTransaction(req.Context(), resp)
if err != nil {
a.Services.Logger.Err(err).Msgf("failed to update transaction - tx: %v", resp)
apirouter.ReturnResponse(w, req, http.StatusInternalServerError, "")
return
}

// Return response
apirouter.ReturnResponse(w, req, http.StatusOK, "")
}
6 changes: 5 additions & 1 deletion actions/transactions/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ type Action struct {

// RegisterRoutes register all the package specific routes
func RegisterRoutes(router *apirouter.Router, appConfig *config.AppConfig, services *config.AppServices) {

// Use the authentication middleware wrapper
a, require := actions.NewStack(appConfig, services)
require.Use(a.RequireAuthentication)
Expand All @@ -22,6 +21,10 @@ func RegisterRoutes(router *apirouter.Router, appConfig *config.AppConfig, servi
aBasic, requireBasic := actions.NewStack(appConfig, services)
requireBasic.Use(aBasic.RequireBasicAuthentication)

// Use the callback middleware wrapper
aCallback, requireCallback := actions.NewStack(appConfig, services)
requireCallback.Use(aCallback.RequireCallbackAuthentication)

// Load the actions and set the services
action := &Action{actions.Action{AppConfig: a.AppConfig, Services: a.Services}}

Expand All @@ -33,4 +36,5 @@ func RegisterRoutes(router *apirouter.Router, appConfig *config.AppConfig, servi
router.HTTPRouter.POST("/"+config.APIVersion+"/transaction", action.Request(router, require.Wrap(action.newTransaction)))
router.HTTPRouter.POST("/"+config.APIVersion+"/transaction/record", action.Request(router, require.Wrap(action.record)))
router.HTTPRouter.POST("/"+config.APIVersion+"/transaction/search", action.Request(router, requireBasic.Wrap(action.search)))
router.HTTPRouter.POST(config.BroadcastCallbackRoute, action.Request(router, requireCallback.Wrap(action.broadcastCallback)))
}
4 changes: 4 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import (
// @securityDefinitions.apikey bux-auth-xpub
// @in header
// @name bux-auth-xpub

// @securityDefinitions.apikey callback-auth
// @in header
// @name authorization
func main() {
defaultLogger := logging.GetDefaultLogger()

Expand Down
11 changes: 9 additions & 2 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,17 @@ new_relic:
nodes:
# deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set
_deployment_id: bux-deployment-id
callback:
callback_host: https://xyz.com
# token to authenticate callback calls - default callback token will be generated from the Admin Key
_callback_token: 44a82509
# protocol - arc/mapi (arc is default)
protocol: arc
# list of apis used for getting and broadcasting transactions
apis:
# gorillapool can be used as well
# - arc_url: https://arc.gorillapool.io
# - token: ""
# - arc_url: https://arc.gorillapool.io
# - token: ""
- arc_url: https://api.taal.com/arc/v1
token: mainnet_06770f425eb00298839a24a49cbdc02c
# use fee quotes for transaction fee calculation
Expand Down Expand Up @@ -135,3 +139,6 @@ server_config:
task_manager:
# task manager factory - memory, redis
factory: memory
# Prometheus metrics configuration
metrics:
enabled: false
Loading
Loading