From cd312818f77b3e88c05d451efc731beca06d8c21 Mon Sep 17 00:00:00 2001 From: p4u Date: Fri, 27 Oct 2023 18:22:10 +0200 Subject: [PATCH] unify faucet URLs Signed-off-by: p4u --- api/faucet/faucet.go | 19 +++++++------------ apiclient/account.go | 2 +- apiclient/helpers.go | 19 ++++++++++++++----- cmd/cli/main.go | 7 ++----- cmd/end2endtest/helpers.go | 8 ++++++-- cmd/end2endtest/main.go | 2 +- cmd/node/main.go | 6 ++---- cmd/voconed/voconed.go | 6 ++---- dockerfiles/testsuite/start_test.sh | 2 +- util/helpers.go | 28 ++++++++++++++++++++++++++++ 10 files changed, 64 insertions(+), 35 deletions(-) diff --git a/api/faucet/faucet.go b/api/faucet/faucet.go index 5b5b2e436..c26cdf5d4 100644 --- a/api/faucet/faucet.go +++ b/api/faucet/faucet.go @@ -22,7 +22,7 @@ const ( // It generates a signed package that can be used to request tokens from the faucet. type FaucetAPI struct { signingKey *ethereum.SignKeys - networks map[string]uint64 + amount uint64 } // AttachFaucetAPI attaches the faucet API to the given http apirest router. @@ -30,14 +30,14 @@ type FaucetAPI struct { // For example, if the pathPrefix is "/faucet", the resulting endpoint is /faucet/{network}/{to}. // The networks map defines the amount of tokens to send for each network. Networks not defined are // considered invalid. -func AttachFaucetAPI(signingKey *ethereum.SignKeys, networks map[string]uint64, +func AttachFaucetAPI(signingKey *ethereum.SignKeys, amount uint64, api *apirest.API, pathPrefix string) error { f := &FaucetAPI{ signingKey: signingKey, - networks: networks, + amount: amount, } return api.RegisterMethod( - path.Join(pathPrefix, "{network}/{to}"), + path.Join(pathPrefix, "{to}"), "GET", apirest.MethodAccessTypePublic, f.faucetHandler, @@ -46,11 +46,6 @@ func AttachFaucetAPI(signingKey *ethereum.SignKeys, networks map[string]uint64, func (f *FaucetAPI) faucetHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { // check network is correct and get amount to send - network := ctx.URLParam("network") - amount, ok := f.networks[network] - if !ok || amount == 0 { - return api.ErrParamNetworkInvalid - } // get TO address toStr := ctx.URLParam("to") if !common.IsHexAddress(toStr) { @@ -59,8 +54,8 @@ func (f *FaucetAPI) faucetHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContex to := common.HexToAddress(toStr) // generate faucet package - log.Debugf("faucet request from %s for network %s", to, network) - fpackage, err := vochain.GenerateFaucetPackage(f.signingKey, to, amount) + log.Debugw("faucet request", "from", to.String(), "amount", f.amount) + fpackage, err := vochain.GenerateFaucetPackage(f.signingKey, to, f.amount) if err != nil { return api.ErrCantGenerateFaucetPkg.WithErr(err) } @@ -73,7 +68,7 @@ func (f *FaucetAPI) faucetHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContex } // send response resp := &FaucetResponse{ - Amount: fmt.Sprint(amount), + Amount: fmt.Sprintf("%d", f.amount), FaucetPackage: fpackageBytes, } data, err := json.Marshal(resp) diff --git a/apiclient/account.go b/apiclient/account.go index 01c099368..3211f4c55 100644 --- a/apiclient/account.go +++ b/apiclient/account.go @@ -408,7 +408,7 @@ func (c *HTTPclient) RegisterSIKForVote(electionId types.HexBytes, proof *Census }, }) if err != nil { - return nil, fmt.Errorf("error enconding RegisterSIKTx: %w", err) + return nil, fmt.Errorf("error encoding RegisterSIKTx: %w", err) } // sign it and send it hash, _, err := c.SignAndSendTx(stx) diff --git a/apiclient/helpers.go b/apiclient/helpers.go index b346a6b2d..d411158c9 100644 --- a/apiclient/helpers.go +++ b/apiclient/helpers.go @@ -15,6 +15,7 @@ import ( "go.vocdoni.io/dvote/httprouter/apirest" "go.vocdoni.io/dvote/log" "go.vocdoni.io/dvote/types" + "go.vocdoni.io/dvote/util" "go.vocdoni.io/proto/build/go/models" "google.golang.org/protobuf/proto" ) @@ -257,8 +258,12 @@ func (c *HTTPclient) EncryptionKeys(electionID types.HexBytes) ([]api.Key, error // GetFaucetPackageFromDevService returns a faucet package. // Needs just the destination wallet address, the URL and bearer token are hardcoded func GetFaucetPackageFromDevService(account string) (*models.FaucetPackage, error) { + url, err := util.BuildURL(DefaultDevelopmentFaucetURL, account) + if err != nil { + return nil, err + } return GetFaucetPackageFromRemoteService( - DefaultDevelopmentFaucetURL+account, + url, "", ) } @@ -271,18 +276,22 @@ func GetFaucetPackageFromRemoteService(faucetURL, token string) (*models.FaucetP if err != nil { return nil, err } + header := http.Header{ + "User-Agent": []string{"Vocdoni API client / 1.0"}, + } + if token != "" { + header.Add("Authorization", "Bearer "+token) + } c := http.Client{} resp, err := c.Do(&http.Request{ Method: HTTPGET, URL: u, - Header: http.Header{ - "Authorization": []string{"Bearer " + token}, - "User-Agent": []string{"Vocdoni API client / 1.0"}, - }, + Header: header, }) if err != nil { return nil, err } + defer resp.Body.Close() if resp.StatusCode != apirest.HTTPstatusOK { return nil, fmt.Errorf("faucet request failed: %s", resp.Status) } diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 8fb2640bc..5dfa15f2f 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -305,11 +305,8 @@ func bootStrapAccount(cli *VocdoniCLI) error { return err } } else { - infoPrint.Printf("trying to fetch faucet package from remote service...\n") - faucetPkg, err = apiclient.GetFaucetPackageFromRemoteService( - apiclient.DefaultDevelopmentFaucetURL+cli.api.MyAddress().Hex(), - "", - ) + infoPrint.Printf("trying to fetch faucet package from default remote service...\n") + faucetPkg, err = apiclient.GetFaucetPackageFromDevService(cli.api.MyAddress().Hex()) if err != nil { return err } diff --git a/cmd/end2endtest/helpers.go b/cmd/end2endtest/helpers.go index 64a2cf80f..1e14ba4f9 100644 --- a/cmd/end2endtest/helpers.go +++ b/cmd/end2endtest/helpers.go @@ -574,11 +574,15 @@ func (t *e2eElection) sendVotes(votes []*apiclient.VoteData) map[int]error { func faucetPackage(faucet, faucetAuthToken, myAddress string) (*models.FaucetPackage, error) { switch faucet { case "": - return nil, fmt.Errorf("need to pass a valid --faucet") + return nil, fmt.Errorf("need to pass a valid URL (--faucet)") case "dev": return apiclient.GetFaucetPackageFromDevService(myAddress) default: - return apiclient.GetFaucetPackageFromRemoteService(faucet+myAddress, faucetAuthToken) + url, err := util.BuildURL(faucet, myAddress) + if err != nil { + return nil, err + } + return apiclient.GetFaucetPackageFromRemoteService(url, faucetAuthToken) } } diff --git a/cmd/end2endtest/main.go b/cmd/end2endtest/main.go index b4ebe18de..e56dc11fe 100644 --- a/cmd/end2endtest/main.go +++ b/cmd/end2endtest/main.go @@ -101,7 +101,7 @@ func main() { fmt.Fprintf(os.Stderr, "\n") } fmt.Fprintf(os.Stderr, "If the network is deployed locally using the docker test suite, ") - fmt.Fprintf(os.Stderr, "the faucet URL might be configured as `--faucet=http://localhost:9090/v2/faucet/dev/`\n") + fmt.Fprintf(os.Stderr, "the faucet URL might be configured as `--faucet=http://localhost:9090/v2/open/claim`\n") } flag.CommandLine.SortFlags = false diff --git a/cmd/node/main.go b/cmd/node/main.go index 838391507..b139aa7a7 100644 --- a/cmd/node/main.go +++ b/cmd/node/main.go @@ -640,11 +640,9 @@ func main() { // attach faucet to the API if enabled if conf.EnableFaucetWithAmount > 0 { if err := faucet.AttachFaucetAPI(srv.Signer, - map[string]uint64{ - conf.Vochain.Chain: conf.EnableFaucetWithAmount, - }, + conf.EnableFaucetWithAmount, uAPI.RouterHandler(), - "/faucet", + "/open/claim", ); err != nil { log.Fatal(err) } diff --git a/cmd/voconed/voconed.go b/cmd/voconed/voconed.go index 5c1d68402..5ceac7d90 100644 --- a/cmd/voconed/voconed.go +++ b/cmd/voconed/voconed.go @@ -250,11 +250,9 @@ func main() { } log.Infof("faucet account %s, faucet amount %d", faucetAccount.Address().Hex(), config.enableFaucetWithAmount) if err := faucet.AttachFaucetAPI(&faucetAccount, - map[string]uint64{ - config.chainID: config.enableFaucetWithAmount, - }, + config.enableFaucetWithAmount, uAPI.RouterHandler(), - "/faucet", + "/open/claim", ); err != nil { log.Fatal(err) } diff --git a/dockerfiles/testsuite/start_test.sh b/dockerfiles/testsuite/start_test.sh index 8e4bf3fcc..c3ec9afef 100755 --- a/dockerfiles/testsuite/start_test.sh +++ b/dockerfiles/testsuite/start_test.sh @@ -33,7 +33,7 @@ cb595f3fa1a4790dd54c139524a1430fc500f95a02affee6a933fcb88849a48d" ACCOUNT_KEYS=${ACCOUNT_KEYS:-$DEFAULT_ACCOUNT_KEYS} GWHOST="http://gateway0:9090/dvote" APIHOST="http://gateway0:9090/v2" -FAUCET="$APIHOST/faucet/dev/" +FAUCET="$APIHOST/open/claim" TEST_PREFIX="testsuite_test" RANDOMID="${RANDOM}${RANDOM}" diff --git a/util/helpers.go b/util/helpers.go index 61c5ccf20..f03f8e422 100644 --- a/util/helpers.go +++ b/util/helpers.go @@ -4,6 +4,9 @@ import ( "crypto/rand" "fmt" "math/big" + "net/url" + "path" + "strings" ) func TrimHex(s string) string { @@ -39,3 +42,28 @@ func RandomInt(min, max int) int { } return int(num.Int64()) + min } + +// BuildURL constructs a URL from parts. It handles any irregularities like missing or extra slashes. +func BuildURL(parts ...string) (string, error) { + if len(parts) == 0 { + return "", fmt.Errorf("no parts provided") + } + + // Parse the base URL (the first part). + base, err := url.Parse(parts[0]) + if err != nil { + return "", fmt.Errorf("could not parse base URL: %v", err) + } + + // Create a path by joining all parts with a "/". This will also clean the path, + otherParts := parts[1:] + for i, part := range otherParts { + cleanPart := path.Clean("/" + part) + otherParts[i] = strings.TrimPrefix(cleanPart, "/") // Remove the leading slash added for cleaning. + } + + // Now, construct the full path with the base path and the other parts. + fullPath := path.Join(base.Path, path.Join(otherParts...)) + base.Path = fullPath + return base.String(), nil +}