From 9d9f3e7dc2706c0fceba7399f6afaadc504cf947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lewandowski?= <35259896+pawellewandowski98@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:39:02 +0100 Subject: [PATCH] feat(223): replace router with gin (#74) --- examples/server/run_server/run_server.go | 11 +-- go.mod | 33 +++++-- go.sum | 91 ++++++++++++++----- server/base.go | 31 ++----- server/capabilities.go | 18 ++-- server/error.go | 31 +------ server/error_test.go | 20 ---- server/p2p_payment_destination.go | 32 +++---- server/p2p_receive_transaction.go | 33 ++++--- .../p2p_receive_transaction_request_parser.go | 5 +- ...p_receive_transaction_request_processor.go | 5 +- server/pki.go | 25 ++--- server/public_profile.go | 20 ++-- server/resolve_address.go | 46 +++++----- server/router.go | 46 ++++------ server/utilitis.go | 42 --------- server/verify.go | 24 ++--- 17 files changed, 220 insertions(+), 293 deletions(-) delete mode 100644 server/error_test.go delete mode 100644 server/utilitis.go diff --git a/examples/server/run_server/run_server.go b/examples/server/run_server/run_server.go index 709bc6f..06ba497 100644 --- a/examples/server/run_server/run_server.go +++ b/examples/server/run_server/run_server.go @@ -1,15 +1,14 @@ package main import ( - "encoding/json" "fmt" + "github.com/gin-gonic/gin" "net/http" "time" "github.com/bitcoin-sv/go-paymail/logging" "github.com/bitcoin-sv/go-paymail/server" - "github.com/julienschmidt/httprouter" ) func main() { @@ -51,15 +50,15 @@ func customCapabilities() map[string]any { "custom_callable_cap": server.CallableCapability{ Path: fmt.Sprintf("/display_paymail/%s", server.PaymailAddressTemplate), Method: http.MethodGet, - Handler: func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - incomingPaymail := p.ByName(server.PaymailAddressParamName) + Handler: func(c *gin.Context) { + incomingPaymail := c.Param(server.PaymailAddressParamName) response := map[string]string{ "paymail": incomingPaymail, } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + c.Header("Content-Type", "application/json") + c.JSON(http.StatusOK, response) }, }, } diff --git a/go.mod b/go.mod index 5b57ebe..5dded73 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21.5 require ( github.com/bitcoinschema/go-bitcoin/v2 v2.0.5 + 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 @@ -11,30 +12,44 @@ require ( github.com/libsv/go-bk v0.1.6 github.com/libsv/go-bt/v2 v2.2.5 github.com/miekg/dns v1.1.57 - github.com/newrelic/go-agent/v3/integrations/nrhttprouter v1.0.2 github.com/rs/zerolog v1.31.0 github.com/stretchr/testify v1.8.4 go.elastic.co/ecszerolog v0.2.0 - golang.org/x/net v0.19.0 + golang.org/x/net v0.21.0 ) require ( github.com/bitcoinsv/bsvd v0.0.0-20190609155523-4c29707f7173 // indirect + github.com/bytedance/sonic v1.10.2 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.18.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/libsv/go-p2p v0.1.5 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/newrelic/go-agent/v3 v3.29.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.7.0 // indirect + golang.org/x/crypto v0.19.0 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.16.1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect - google.golang.org/grpc v1.60.1 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7855ea4..a386471 100644 --- a/go.sum +++ b/go.sum @@ -2,29 +2,59 @@ github.com/bitcoinschema/go-bitcoin/v2 v2.0.5 h1:Sgh5Eb746Zck/46rFDrZZEXZWyO53fM github.com/bitcoinschema/go-bitcoin/v2 v2.0.5/go.mod h1:JjO1ivfZv6vhK0uAXzyH08AAHlzNMAfnyK1Fiv9r4ZA= github.com/bitcoinsv/bsvd v0.0.0-20190609155523-4c29707f7173 h1:2yTIV9u7H0BhRDGXH5xrAwAz7XibWJtX2dNezMeNsUo= github.com/bitcoinsv/bsvd v0.0.0-20190609155523-4c29707f7173/go.mod h1:BZ1UcC9+tmcDEcdVXgpt13hMczwJxWzpAn68wNs7zRA= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= +github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= +github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo= -github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= 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= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/libsv/go-bc v0.1.25 h1:Jsmqrari/YzVH3FuDfvSeG6nyDiOGCV0AYntaL/5TFk= github.com/libsv/go-bc v0.1.25/go.mod h1:l6epTfcakN8YKId/hrpUzlu1QeT3ODF1MI3DeYhG1O8= github.com/libsv/go-bk v0.1.6 h1:c9CiT5+64HRDbzxPl1v/oiFmbvWZTuUYqywCf+MBs/c= @@ -43,10 +73,13 @@ github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04 github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= -github.com/newrelic/go-agent/v3 v3.29.0 h1:Bc1D3DoOkpJs6aIzhOjUp+yIKJ2RfZ+LMQemZOs9t9k= -github.com/newrelic/go-agent/v3 v3.29.0/go.mod h1:9utrgxlSryNqRrTvII2XBL+0lpofXbqXApvVWPpbzUg= -github.com/newrelic/go-agent/v3/integrations/nrhttprouter v1.0.2 h1:Y+bKuryqCg+TAvBkBaEKwak+Boy5OdosQNdDGFwyDLo= -github.com/newrelic/go-agent/v3/integrations/nrhttprouter v1.0.2/go.mod h1:lhRXsOMo2UYHbtvb2xdtbTdBaL4ovLAdhW8ky3NNyAQ= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -57,17 +90,30 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.elastic.co/ecszerolog v0.2.0 h1:nbX4dQ08jb3+vsvACfmzAqGDoBh8F2HQDUgpqwAVTg0= go.elastic.co/ecszerolog v0.2.0/go.mod h1:wR5Mv0BVQJ17LopUX5Fd0LLKCC9iF++58iKY+lL09lc= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= +golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= @@ -78,8 +124,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -96,8 +142,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -120,18 +166,13 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/server/base.go b/server/base.go index 6deb368..1464e2c 100644 --- a/server/base.go +++ b/server/base.go @@ -1,39 +1,20 @@ package server import ( - "encoding/json" + "github.com/gin-gonic/gin" "net/http" - - "github.com/julienschmidt/httprouter" ) // index basic request to / // nolint: revive // do not check for unused param required by interface -func index(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { +func index(c *gin.Context) { responseData := map[string]interface{}{"message": "Welcome to the Paymail Server ✌(◕‿-)✌"} - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - err := json.NewEncoder(w).Encode(responseData) - - if err != nil { - ErrorResponse(w, req, ErrorEncodingResponse, err.Error(), http.StatusInternalServerError, nil) - return - } + c.Header("Content-Type", "application/json") + c.JSON(http.StatusOK, responseData) } // health is a basic request to return a health response -func health(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) { - w.WriteHeader(http.StatusOK) -} - -// notFound handles all 404 requests -// nolint: revive // do not check for unused param required by interface -func notFound(w http.ResponseWriter, req *http.Request) { - ErrorResponse(w, req, ErrorRequestNotFound, "request not found", http.StatusNotFound, nil) -} - -// methodNotAllowed handles all 405 requests -func methodNotAllowed(w http.ResponseWriter, req *http.Request) { - ErrorResponse(w, req, ErrorMethodNotFound, "method "+req.Method+" not allowed", http.StatusMethodNotAllowed, nil) +func health(c *gin.Context) { + c.Status(http.StatusOK) } diff --git a/server/capabilities.go b/server/capabilities.go index bfd3bc4..b74a043 100644 --- a/server/capabilities.go +++ b/server/capabilities.go @@ -2,17 +2,17 @@ package server import ( "fmt" + "github.com/gin-gonic/gin" "net/http" "strings" "github.com/bitcoin-sv/go-paymail" - "github.com/julienschmidt/httprouter" ) type CallableCapability struct { Path string Method string - Handler httprouter.Handle + Handler gin.HandlerFunc } type CallableCapabilitiesMap map[string]CallableCapability @@ -89,28 +89,28 @@ func _addCapabilities[T any](base map[string]T, newCaps map[string]T) { // and list all active capabilities of the Paymail server // // Specs: http://bsvalias.org/02-02-capability-discovery.html -func (c *Configuration) showCapabilities(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { +func (c *Configuration) showCapabilities(context *gin.Context) { // Check the host (allowed, and used for capabilities response) // todo: bake this into middleware? This is protecting the "req" host name (like CORs) host := "" - if req.URL.IsAbs() || len(req.URL.Host) == 0 { - host = req.Host + if context.Request.URL.IsAbs() || len(context.Request.URL.Host) == 0 { + host = context.Request.Host } else { - host = req.URL.Host + host = context.Request.URL.Host } if !c.IsAllowedDomain(host) { - ErrorResponse(w, req, ErrorUnknownDomain, "domain unknown: "+host, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorUnknownDomain, "domain unknown: "+host, http.StatusBadRequest) return } capabilities, err := c.EnrichCapabilities(host) if err != nil { - ErrorResponse(w, req, ErrorEncodingResponse, err.Error(), http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorEncodingResponse, err.Error(), http.StatusBadRequest) return } - writeJsonResponse(w, req, c.Logger, capabilities) + context.JSON(http.StatusOK, capabilities) } // EnrichCapabilities will update the capabilities with the appropriate service url diff --git a/server/error.go b/server/error.go index da63d59..963a98b 100644 --- a/server/error.go +++ b/server/error.go @@ -1,14 +1,9 @@ package server import ( - "encoding/json" "errors" - "net/http" - - "github.com/bitcoin-sv/go-paymail/logging" - "github.com/rs/zerolog" - "github.com/bitcoin-sv/go-paymail" + "github.com/gin-gonic/gin" ) // Error codes for server response errors @@ -61,27 +56,7 @@ var ( // ErrorResponse is a standard way to return errors to the client // // Specs: http://bsvalias.org/99-01-recommendations.html -func ErrorResponse(w http.ResponseWriter, req *http.Request, code, message string, statusCode int, log *zerolog.Logger) { - if log == nil { - log = logging.GetDefaultLogger() - } - +func ErrorResponse(c *gin.Context, code, message string, statusCode int) { srvErr := &paymail.ServerError{Code: code, Message: message} - jsonData, err := json.Marshal(srvErr) - - if err != nil { - log.Debug(). - Str("logger", "http-error"). - Msgf("%d | %s | %s | %s | %s", http.StatusInternalServerError, req.RemoteAddr, req.Method, req.URL, message) - http.Error(w, ErrorFailedMarshalJSON, http.StatusInternalServerError) - return - } - - errorLogger := log.With(). - Str("logger", "http-error"). - Str("code", code). - Str("msg", message). - Logger() - - writeResponse(w, req, &errorLogger, statusCode, "application/json", jsonData) + c.JSON(statusCode, srvErr) } diff --git a/server/error_test.go b/server/error_test.go deleted file mode 100644 index 96e141a..0000000 --- a/server/error_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package server - -import ( - "github.com/bitcoin-sv/go-paymail/logging" - "net/http" - "net/http/httptest" - "testing" -) - -// TestErrorResponse will test the method ErrorResponse() -func TestErrorResponse(t *testing.T) { - t.Run("placeholder test", func(t *testing.T) { - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/", nil) - log := logging.GetDefaultLogger() - ErrorResponse(w, req, ErrorMethodNotFound, "test message", http.StatusBadRequest, log) - - // todo: actually test the error response - }) -} diff --git a/server/p2p_payment_destination.go b/server/p2p_payment_destination.go index 8d6d681..a573ea4 100644 --- a/server/p2p_payment_destination.go +++ b/server/p2p_payment_destination.go @@ -1,11 +1,10 @@ package server import ( - "encoding/json" + "github.com/gin-gonic/gin" "net/http" "github.com/bitcoin-sv/go-paymail" - "github.com/julienschmidt/httprouter" ) /* @@ -22,22 +21,22 @@ type p2pDestinationRequestBody struct { // p2pDestination will return an output script(s) for a destination (used with SendP2PTransaction) // // Specs: https://docs.moneybutton.com/docs/paymail-07-p2p-payment-destination.html -func (c *Configuration) p2pDestination(w http.ResponseWriter, req *http.Request, p httprouter.Params) { - incomingPaymail := p.ByName(PaymailAddressParamName) +func (c *Configuration) p2pDestination(context *gin.Context) { + incomingPaymail := context.Param(PaymailAddressParamName) // Parse, sanitize and basic validation alias, domain, paymailAddress := paymail.SanitizePaymail(incomingPaymail) if len(paymailAddress) == 0 { - ErrorResponse(w, req, ErrorInvalidParameter, "invalid paymail: "+incomingPaymail, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidParameter, "invalid paymail: "+incomingPaymail, http.StatusBadRequest) return } else if !c.IsAllowedDomain(domain) { - ErrorResponse(w, req, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest) return } var b p2pDestinationRequestBody - err := json.NewDecoder(req.Body).Decode(&b) + err := context.Bind(&b) if err != nil { - ErrorResponse(w, req, ErrorInvalidParameter, "error decoding body: "+err.Error(), http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidParameter, "error decoding body: "+err.Error(), http.StatusBadRequest) return } @@ -48,33 +47,32 @@ func (c *Configuration) p2pDestination(w http.ResponseWriter, req *http.Request, // Did we get some satoshis? if paymentRequest.Satoshis == 0 { - ErrorResponse(w, req, ErrorMissingField, "missing parameter: satoshis", http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorMissingField, "missing parameter: satoshis", http.StatusBadRequest) return } // Create the metadata struct - md := CreateMetadata(req, alias, domain, "") + md := CreateMetadata(context.Request, alias, domain, "") md.PaymentDestination = paymentRequest // Get from the data layer - foundPaymail, err := c.actions.GetPaymailByAlias(req.Context(), alias, domain, md) + foundPaymail, err := c.actions.GetPaymailByAlias(context.Request.Context(), alias, domain, md) if err != nil { - ErrorResponse(w, req, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed) return } else if foundPaymail == nil { - ErrorResponse(w, req, ErrorPaymailNotFound, "paymail not found", http.StatusNotFound, c.Logger) + ErrorResponse(context, ErrorPaymailNotFound, "paymail not found", http.StatusNotFound) return } // Create the response var response *paymail.PaymentDestinationPayload if response, err = c.actions.CreateP2PDestinationResponse( - req.Context(), alias, domain, paymentRequest.Satoshis, md, + context.Request.Context(), alias, domain, paymentRequest.Satoshis, md, ); err != nil { - ErrorResponse(w, req, ErrorScript, "error creating output script(s): "+err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorScript, "error creating output script(s): "+err.Error(), http.StatusExpectationFailed) return } - // Set the response - writeJsonResponse(w, req, c.Logger, response) + context.JSON(http.StatusOK, response) } diff --git a/server/p2p_receive_transaction.go b/server/p2p_receive_transaction.go index 092925f..b455829 100644 --- a/server/p2p_receive_transaction.go +++ b/server/p2p_receive_transaction.go @@ -1,11 +1,11 @@ package server import ( + "github.com/gin-gonic/gin" "net/http" "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/go-paymail/spv" - "github.com/julienschmidt/httprouter" ) type p2pPayloadFormat uint @@ -32,12 +32,14 @@ Incoming Data Object Example: // p2pReceiveTx will receive a P2P transaction (from previous request: P2P Payment Destination) // // Specs: https://docs.moneybutton.com/docs/paymail-06-p2p-transactions.html -func (c *Configuration) p2pReceiveTx(w http.ResponseWriter, req *http.Request, p httprouter.Params) { +func (c *Configuration) p2pReceiveTx(context *gin.Context) { p2pFormat := basicP2pPayload - requestPayload, _, md, vErr := processP2pReceiveTxRequest(c, req, p, p2pFormat) + incomingPaymail := context.Param(PaymailAddressParamName) + + requestPayload, _, md, vErr := processP2pReceiveTxRequest(c, context.Request, incomingPaymail, p2pFormat) if vErr != nil { - ErrorResponse(w, req, vErr.code, vErr.msg, vErr.httpResponseCode, c.Logger) + ErrorResponse(context, vErr.code, vErr.msg, vErr.httpResponseCode) return } @@ -48,13 +50,13 @@ func (c *Configuration) p2pReceiveTx(w http.ResponseWriter, req *http.Request, p var response *paymail.P2PTransactionPayload var err error if response, err = c.actions.RecordTransaction( - req.Context(), requestPayload.P2PTransaction, md, + context.Request.Context(), requestPayload.P2PTransaction, md, ); err != nil { - ErrorResponse(w, req, ErrorRecordingTx, err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorRecordingTx, err.Error(), http.StatusExpectationFailed) return } - writeJsonResponse(w, req, c.Logger, response) + context.JSON(http.StatusOK, response) } /* @@ -71,12 +73,13 @@ Incoming Data Object Example: } */ // p2pReceiveBeefTx will receive a P2P transaction in BEEF format -func (c *Configuration) p2pReceiveBeefTx(w http.ResponseWriter, req *http.Request, p httprouter.Params) { +func (c *Configuration) p2pReceiveBeefTx(context *gin.Context) { p2pFormat := beefP2pPayload + incomingPaymail := context.Param(PaymailAddressParamName) - requestPayload, dBeef, md, vErr := processP2pReceiveTxRequest(c, req, p, p2pFormat) + requestPayload, dBeef, md, vErr := processP2pReceiveTxRequest(c, context.Request, incomingPaymail, p2pFormat) if vErr != nil { - ErrorResponse(w, req, vErr.code, vErr.msg, vErr.httpResponseCode, c.Logger) + ErrorResponse(context, vErr.code, vErr.msg, vErr.httpResponseCode) return } @@ -88,19 +91,19 @@ func (c *Configuration) p2pReceiveBeefTx(w http.ResponseWriter, req *http.Reques panic("empty beef after parsing!") } - err := spv.ExecuteSimplifiedPaymentVerification(req.Context(), dBeef, c.actions) + err := spv.ExecuteSimplifiedPaymentVerification(context.Request.Context(), dBeef, c.actions) if err != nil { - ErrorResponse(w, req, ErrorSimplifiedPaymentVerification, err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorSimplifiedPaymentVerification, err.Error(), http.StatusExpectationFailed) return } var response *paymail.P2PTransactionPayload if response, err = c.actions.RecordTransaction( - req.Context(), requestPayload.P2PTransaction, md, + context.Request.Context(), requestPayload.P2PTransaction, md, ); err != nil { - ErrorResponse(w, req, ErrorRecordingTx, err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorRecordingTx, err.Error(), http.StatusExpectationFailed) return } - writeJsonResponse(w, req, c.Logger, response) + context.JSON(http.StatusOK, response) } diff --git a/server/p2p_receive_transaction_request_parser.go b/server/p2p_receive_transaction_request_parser.go index f657a29..c33225b 100644 --- a/server/p2p_receive_transaction_request_parser.go +++ b/server/p2p_receive_transaction_request_parser.go @@ -5,16 +5,13 @@ import ( "net/http" "github.com/bitcoin-sv/go-paymail" - "github.com/julienschmidt/httprouter" ) type parseError struct { code, msg string } -func parseP2pReceiveTxRequest(c *Configuration, req *http.Request, params httprouter.Params, format p2pPayloadFormat) (*p2pReceiveTxReqPayload, *parseError) { - incomingPaymail := params.ByName(PaymailAddressParamName) - +func parseP2pReceiveTxRequest(c *Configuration, req *http.Request, incomingPaymail string, format p2pPayloadFormat) (*p2pReceiveTxReqPayload, *parseError) { alias, domain, paymailAddress := paymail.SanitizePaymail(incomingPaymail) if len(paymailAddress) == 0 { return nil, &parseError{ErrorInvalidParameter, "invalid paymail: " + incomingPaymail} diff --git a/server/p2p_receive_transaction_request_processor.go b/server/p2p_receive_transaction_request_processor.go index 7c67092..c22059c 100644 --- a/server/p2p_receive_transaction_request_processor.go +++ b/server/p2p_receive_transaction_request_processor.go @@ -7,7 +7,6 @@ import ( "net/http" "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/julienschmidt/httprouter" "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" @@ -25,10 +24,10 @@ type processingError struct { httpResponseCode int } -func processP2pReceiveTxRequest(c *Configuration, req *http.Request, p httprouter.Params, format p2pPayloadFormat) ( +func processP2pReceiveTxRequest(c *Configuration, req *http.Request, incomingPaymail string, format p2pPayloadFormat) ( *p2pReceiveTxReqPayload, *beef.DecodedBEEF, *RequestMetadata, *processingError, ) { - payload, vErr := parseP2pReceiveTxRequest(c, req, p, format) + payload, vErr := parseP2pReceiveTxRequest(c, req, incomingPaymail, format) if vErr != nil { return returnError(&processingError{vErr, http.StatusBadRequest}) } diff --git a/server/pki.go b/server/pki.go index 6b1fe4f..6d10c29 100644 --- a/server/pki.go +++ b/server/pki.go @@ -1,39 +1,35 @@ package server import ( + "github.com/gin-gonic/gin" "net/http" "github.com/bitcoin-sv/go-paymail" - "github.com/julienschmidt/httprouter" ) // showPKI will return the public key information for the corresponding paymail address // // Specs: http://bsvalias.org/03-public-key-infrastructure.html -func (c *Configuration) showPKI(w http.ResponseWriter, req *http.Request, p httprouter.Params) { +func (c *Configuration) showPKI(context *gin.Context) { + incomingPaymail := context.Param(PaymailAddressParamName) - incomingPaymail := p.ByName(PaymailAddressParamName) - - // Parse, sanitize and basic validation alias, domain, address := paymail.SanitizePaymail(incomingPaymail) if len(address) == 0 { - ErrorResponse(w, req, ErrorInvalidParameter, "domain unknown: "+domain, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidParameter, "domain unknown: "+domain, http.StatusBadRequest) return } else if !c.IsAllowedDomain(domain) { - ErrorResponse(w, req, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest) return } - // Create the metadata struct - md := CreateMetadata(req, alias, domain, "") + md := CreateMetadata(context.Request, alias, domain, "") - // Get from the data layer - foundPaymail, err := c.actions.GetPaymailByAlias(req.Context(), alias, domain, md) + foundPaymail, err := c.actions.GetPaymailByAlias(context.Request.Context(), alias, domain, md) if err != nil { - ErrorResponse(w, req, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed) return } else if foundPaymail == nil { - ErrorResponse(w, req, ErrorPaymailNotFound, "paymail not found: "+incomingPaymail, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorPaymailNotFound, "paymail not found: "+incomingPaymail, http.StatusBadRequest) return } @@ -43,6 +39,5 @@ func (c *Configuration) showPKI(w http.ResponseWriter, req *http.Request, p http PubKey: foundPaymail.PubKey, } - // Set the response - writeJsonResponse(w, req, c.Logger, pkiPayload) + context.JSON(http.StatusOK, pkiPayload) } diff --git a/server/public_profile.go b/server/public_profile.go index d23ae9f..9117c98 100644 --- a/server/public_profile.go +++ b/server/public_profile.go @@ -1,38 +1,38 @@ package server import ( + "github.com/gin-gonic/gin" "net/http" "github.com/bitcoin-sv/go-paymail" - "github.com/julienschmidt/httprouter" ) // publicProfile will return the public profile for the corresponding paymail address // // Specs: https://github.com/bitcoin-sv-specs/brfc-paymail/pull/7/files -func (c *Configuration) publicProfile(w http.ResponseWriter, req *http.Request, p httprouter.Params) { - incomingPaymail := p.ByName(PaymailAddressParamName) +func (c *Configuration) publicProfile(context *gin.Context) { + incomingPaymail := context.Param(PaymailAddressParamName) // Parse, sanitize and basic validation alias, domain, address := paymail.SanitizePaymail(incomingPaymail) if len(address) == 0 { - ErrorResponse(w, req, ErrorInvalidParameter, "invalid paymail: "+incomingPaymail, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidParameter, "invalid paymail: "+incomingPaymail, http.StatusBadRequest) return } else if !c.IsAllowedDomain(domain) { - ErrorResponse(w, req, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest) return } // Create the metadata struct - md := CreateMetadata(req, alias, domain, "") + md := CreateMetadata(context.Request, alias, domain, "") // Get from the data layer - foundPaymail, err := c.actions.GetPaymailByAlias(req.Context(), alias, domain, md) + foundPaymail, err := c.actions.GetPaymailByAlias(context.Request.Context(), alias, domain, md) if err != nil { - ErrorResponse(w, req, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed) return } else if foundPaymail == nil { - ErrorResponse(w, req, ErrorPaymailNotFound, "paymail not found", http.StatusNotFound, c.Logger) + ErrorResponse(context, ErrorPaymailNotFound, "paymail not found", http.StatusNotFound) return } @@ -42,5 +42,5 @@ func (c *Configuration) publicProfile(w http.ResponseWriter, req *http.Request, } // Set the response - writeJsonResponse(w, req, c.Logger, payload) + context.JSON(http.StatusOK, payload) } diff --git a/server/resolve_address.go b/server/resolve_address.go index f512625..3e69e33 100644 --- a/server/resolve_address.go +++ b/server/resolve_address.go @@ -1,14 +1,13 @@ package server import ( - "encoding/json" + "github.com/gin-gonic/gin" "net" "net/http" "time" "github.com/bitcoin-sv/go-paymail" "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/julienschmidt/httprouter" "github.com/libsv/go-bk/bec" "github.com/libsv/go-bt/v2/bscript" ) @@ -28,44 +27,45 @@ Incoming Data Object Example: // resolveAddress will return the payment destination (bitcoin address) for the corresponding paymail address // // Specs: http://bsvalias.org/04-01-basic-address-resolution.html -func (c *Configuration) resolveAddress(w http.ResponseWriter, req *http.Request, p httprouter.Params) { - incomingPaymail := p.ByName(PaymailAddressParamName) +func (c *Configuration) resolveAddress(context *gin.Context) { + incomingPaymail := context.Param(PaymailAddressParamName) // Parse, sanitize and basic validation alias, domain, paymailAddress := paymail.SanitizePaymail(incomingPaymail) if len(paymailAddress) == 0 { - ErrorResponse(w, req, ErrorInvalidParameter, "invalid paymail: "+incomingPaymail, http.StatusBadRequest, c.Logger) + context.JSON(http.StatusBadRequest, "invalid paymail: "+incomingPaymail) + ErrorResponse(context, ErrorInvalidParameter, "invalid paymail: "+incomingPaymail, http.StatusBadRequest) return } else if !c.IsAllowedDomain(domain) { - ErrorResponse(w, req, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest) return } var senderRequest paymail.SenderRequest - err := json.NewDecoder(req.Body).Decode(&senderRequest) + err := context.Bind(&senderRequest) if err != nil { - ErrorResponse(w, req, ErrorInvalidParameter, "invalid request body: "+err.Error(), http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidParameter, "invalid request body: "+err.Error(), http.StatusBadRequest) return } // Check for required fields if len(senderRequest.SenderHandle) == 0 { - ErrorResponse(w, req, ErrorInvalidSenderHandle, "senderHandle is empty", http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidSenderHandle, "senderHandle is empty", http.StatusBadRequest) return } else if len(senderRequest.Dt) == 0 { - ErrorResponse(w, req, ErrorInvalidDt, "dt is empty", http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidDt, "dt is empty", http.StatusBadRequest) return } // Validate the timestamp if err = paymail.ValidateTimestamp(senderRequest.Dt); err != nil { - ErrorResponse(w, req, ErrorInvalidDt, "invalid dt: "+err.Error(), http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidDt, "invalid dt: "+err.Error(), http.StatusBadRequest) return } // Basic validation on sender handle if err = paymail.ValidatePaymail(senderRequest.SenderHandle); err != nil { - ErrorResponse(w, req, ErrorInvalidSenderHandle, "invalid senderHandle: "+err.Error(), http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidSenderHandle, "invalid senderHandle: "+err.Error(), http.StatusBadRequest) return } @@ -77,53 +77,53 @@ func (c *Configuration) resolveAddress(w http.ResponseWriter, req *http.Request, var senderPubKey *bec.PublicKey senderPubKey, err = getSenderPubKey(senderRequest.SenderHandle) if err != nil { - ErrorResponse(w, req, ErrorInvalidSenderHandle, "invalid senderHandle: "+err.Error(), http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidSenderHandle, "invalid senderHandle: "+err.Error(), http.StatusBadRequest) return } // Derive address from pubKey var rawAddress *bscript.Address if rawAddress, err = bitcoin.GetAddressFromPubKey(senderPubKey, true); err != nil { - ErrorResponse(w, req, ErrorInvalidSenderHandle, "invalid senderHandle: "+err.Error(), http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidSenderHandle, "invalid senderHandle: "+err.Error(), http.StatusBadRequest) return } // Verify the signature if err = senderRequest.Verify(rawAddress.AddressString, senderRequest.Signature); err != nil { - ErrorResponse(w, req, ErrorInvalidSignature, "invalid signature: "+err.Error(), http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidSignature, "invalid signature: "+err.Error(), http.StatusBadRequest) return } } else { - ErrorResponse(w, req, ErrorInvalidSignature, "missing required signature", http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidSignature, "missing required signature", http.StatusBadRequest) return } } // Create the metadata struct - md := CreateMetadata(req, alias, domain, "") + md := CreateMetadata(context.Request, alias, domain, "") md.ResolveAddress = &senderRequest // Get from the data layer - foundPaymail, err := c.actions.GetPaymailByAlias(req.Context(), alias, domain, md) + foundPaymail, err := c.actions.GetPaymailByAlias(context.Request.Context(), alias, domain, md) if err != nil { - ErrorResponse(w, req, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed) return } else if foundPaymail == nil { - ErrorResponse(w, req, ErrorPaymailNotFound, "paymail not found", http.StatusNotFound, c.Logger) + ErrorResponse(context, ErrorPaymailNotFound, "paymail not found", http.StatusNotFound) return } // Get the resolution information var response *paymail.ResolutionPayload if response, err = c.actions.CreateAddressResolutionResponse( - req.Context(), alias, domain, c.SenderValidationEnabled, md, + context.Request.Context(), alias, domain, c.SenderValidationEnabled, md, ); err != nil { - ErrorResponse(w, req, ErrorScript, "error creating output script: "+err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorScript, "error creating output script: "+err.Error(), http.StatusExpectationFailed) return } // Set the response - writeJsonResponse(w, req, c.Logger, response) + context.JSON(http.StatusOK, response) } // getSenderPubKey will fetch the pubKey from a PKI request for the sender handle diff --git a/server/router.go b/server/router.go index 603584a..ea077e2 100644 --- a/server/router.go +++ b/server/router.go @@ -2,24 +2,23 @@ package server import ( "fmt" - "net/http" + "github.com/gin-gonic/gin" "strings" - - "github.com/newrelic/go-agent/v3/integrations/nrhttprouter" ) // Handlers are used to isolate loading the routes (used for testing) -func Handlers(configuration *Configuration) *nrhttprouter.Router { - router := nrhttprouter.New(nil) +func Handlers(configuration *Configuration) *gin.Engine { + engine := gin.New() + engine.Use(gin.LoggerWithWriter(configuration.Logger), gin.Recovery()) - configuration.RegisterBasicRoutes(router) - configuration.RegisterRoutes(router) + configuration.RegisterBasicRoutes(engine) + configuration.RegisterRoutes(engine) - return router + return engine } // RegisterBasicRoutes register the basic routes to the http router -func (c *Configuration) RegisterBasicRoutes(router *nrhttprouter.Router) { +func (c *Configuration) RegisterBasicRoutes(engine *gin.Engine) { // Skip if not set if c.BasicRoutes == nil { return @@ -27,42 +26,29 @@ func (c *Configuration) RegisterBasicRoutes(router *nrhttprouter.Router) { // Set the main index page (navigating to slash) if c.BasicRoutes.AddIndexRoute { - router.GET("/", index) + engine.GET("/", index) // router.OPTIONS("/", router.SetCrossOriginHeaders) // Disabled for security } // Set the health request (used for load balancers) if c.BasicRoutes.AddHealthRoute { - router.GET("/health", health) - router.OPTIONS("/health", health) - router.HEAD("/health", health) - } - - // Set the 404 handler (any request not detected) - if c.BasicRoutes.Add404Route { - router.NotFound = http.HandlerFunc(notFound) - } - - // Set the method not allowed - if c.BasicRoutes.AddNotAllowed { - router.MethodNotAllowed = http.HandlerFunc(methodNotAllowed) + engine.GET("/health", health) + engine.OPTIONS("/health", health) + engine.HEAD("/health", health) } } // RegisterRoutes register all the available paymail routes to the http router -func (c *Configuration) RegisterRoutes(router *nrhttprouter.Router) { - router.GET("/.well-known/"+c.ServiceName, c.showCapabilities) // service discovery +func (c *Configuration) RegisterRoutes(engine *gin.Engine) { + engine.GET("/.well-known/"+c.ServiceName, c.showCapabilities) // service discovery - for key, cap := range c.callableCapabilities { + for _, cap := range c.callableCapabilities { routerPath := c.templateToRouterPath(cap.Path) - router.Handle( + engine.Handle( cap.Method, routerPath, cap.Handler, ) - - c.Logger.Info().Msgf("Registering endpoint for capability: %s", key) - c.Logger.Debug().Msgf("Endpoint[%s]: %s %s", key, cap.Method, routerPath) } } diff --git a/server/utilitis.go b/server/utilitis.go deleted file mode 100644 index 01fbe29..0000000 --- a/server/utilitis.go +++ /dev/null @@ -1,42 +0,0 @@ -package server - -import ( - "encoding/json" - "github.com/rs/zerolog" - "net/http" -) - -func writeJsonResponse(w http.ResponseWriter, req *http.Request, log *zerolog.Logger, response any) { - if response == nil { - panic("writeJsonRespone: empty response data") - } - - jsonData, err := json.Marshal(response) - if err != nil { - ErrorResponse(w, req, ErrorFailedMarshalJSON, "failed to marshal JSON response", http.StatusInternalServerError, log) - return - } - - responseLogger := log.With().Str("logger", "http-response").Logger() - - writeResponse(w, req, &responseLogger, http.StatusOK, "application/json", jsonData) -} - -func writeResponse(w http.ResponseWriter, req *http.Request, log *zerolog.Logger, statusCode int, contentType string, responseData []byte) { - w.Header().Set("Content-Type", contentType) - w.WriteHeader(statusCode) - - log.Debug(). - Str("method", req.Method). - Int("status", statusCode). - Str("remote", req.RemoteAddr). - Str("url", req.URL.String()). - Msgf("%d | %s | %s | %s ", statusCode, req.RemoteAddr, req.Method, req.URL) - - if responseData != nil { - _, err := w.Write(responseData) - if err != nil { - panic("writeResponse: " + err.Error()) - } - } -} diff --git a/server/verify.go b/server/verify.go index e86ed33..357f78f 100644 --- a/server/verify.go +++ b/server/verify.go @@ -1,45 +1,45 @@ package server import ( + "github.com/gin-gonic/gin" "net/http" "github.com/bitcoin-sv/go-paymail" - "github.com/julienschmidt/httprouter" ) // verifyPubKey will return a response if the pubkey matches the paymail given // // Specs: https://bsvalias.org/05-verify-public-key-owner.html -func (c *Configuration) verifyPubKey(w http.ResponseWriter, req *http.Request, p httprouter.Params) { - incomingPaymail := p.ByName(PaymailAddressParamName) - incomingPubKey := p.ByName(PubKeyParamName) +func (c *Configuration) verifyPubKey(context *gin.Context) { + incomingPaymail := context.Param(PaymailAddressParamName) + incomingPubKey := context.Param(PubKeyParamName) // Parse, sanitize and basic validation alias, domain, address := paymail.SanitizePaymail(incomingPaymail) if len(address) == 0 { - ErrorResponse(w, req, ErrorInvalidParameter, "invalid paymail: "+incomingPaymail, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidParameter, "invalid paymail: "+incomingPaymail, http.StatusBadRequest) return } else if !c.IsAllowedDomain(domain) { - ErrorResponse(w, req, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorUnknownDomain, "domain unknown: "+domain, http.StatusBadRequest) return } // Basic validation on pubkey if len(incomingPubKey) != paymail.PubKeyLength { - ErrorResponse(w, req, ErrorInvalidPubKey, "invalid pubkey: "+incomingPubKey, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorInvalidPubKey, "invalid pubkey: "+incomingPubKey, http.StatusBadRequest) return } // Create the metadata struct - md := CreateMetadata(req, alias, domain, "") + md := CreateMetadata(context.Request, alias, domain, "") // Get from the data layer - foundPaymail, err := c.actions.GetPaymailByAlias(req.Context(), alias, domain, md) + foundPaymail, err := c.actions.GetPaymailByAlias(context.Request.Context(), alias, domain, md) if err != nil { - ErrorResponse(w, req, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed, c.Logger) + ErrorResponse(context, ErrorFindingPaymail, err.Error(), http.StatusExpectationFailed) return } else if foundPaymail == nil { - ErrorResponse(w, req, ErrorPaymailNotFound, "paymail not found: "+incomingPaymail, http.StatusBadRequest, c.Logger) + ErrorResponse(context, ErrorPaymailNotFound, "paymail not found: "+incomingPaymail, http.StatusBadRequest) return } @@ -50,5 +50,5 @@ func (c *Configuration) verifyPubKey(w http.ResponseWriter, req *http.Request, p Match: foundPaymail.PubKey == incomingPubKey, } - writeJsonResponse(w, req, c.Logger, verPayload) + context.JSON(http.StatusOK, verPayload) }