From 5c90a86bf696de6ba865ce83a9386da7bd074c46 Mon Sep 17 00:00:00 2001 From: Dennis B Date: Sun, 29 Sep 2024 02:44:32 +0000 Subject: [PATCH] Added CORS support [#3624] Added support for CORS to link up simulator with webpage frontends, such as the explorer Changelog: feature Closes #3624 --- Makefile | 11 ++++- tools/cmd/simulator/Dockerfile | 25 +++++++++++ tools/cmd/simulator/main.go | 81 +++++++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 tools/cmd/simulator/Dockerfile diff --git a/Makefile b/Makefile index fe683d6d6..43e6afc07 100644 --- a/Makefile +++ b/Makefile @@ -35,4 +35,13 @@ faucet-docker: docker build --build-arg "GIT_DESCRIBE=$(GIT_DESCRIBE)" --build-arg "GIT_COMMIT=$(GIT_COMMIT)" -t "$(IMAGE)/faucet" -f cmd/accumulated-faucet/Dockerfile . faucet-docker-push: faucet-docker - docker push "$(IMAGE)/faucet" \ No newline at end of file + docker push "$(IMAGE)/faucet" + +sim: + go build -trimpath $(FLAGS) ./tools/cmd/simulator + +sim-docker: + docker build --build-arg "GIT_DESCRIBE=$(GIT_DESCRIBE)" --build-arg "GIT_COMMIT=$(GIT_COMMIT)" -t "$(IMAGE)/simulator" -f tools/cmd/simulator/Dockerfile . + +sim-docker-push: sim-docker + docker push "$(IMAGE)/simulator" \ No newline at end of file diff --git a/tools/cmd/simulator/Dockerfile b/tools/cmd/simulator/Dockerfile new file mode 100644 index 000000000..9708622c6 --- /dev/null +++ b/tools/cmd/simulator/Dockerfile @@ -0,0 +1,25 @@ +FROM golang:1.23 as build + +ARG GIT_DESCRIBE +ARG GIT_COMMIT + +# Build +WORKDIR /root +COPY . . +ENV CGO_ENABLED 0 +RUN make sim GIT_DESCRIBE=$GIT_DESCRIBE GIT_COMMIT=$GIT_COMMIT +RUN go install github.com/go-delve/delve/cmd/dlv@latest +RUN mkdir /data + +FROM alpine:3 + +# Install tools +RUN apk add --no-cache bash jq curl nano + +# Copy binaries +COPY --from=build /root/simulator /go/bin/dlv /bin/ + +EXPOSE 26660 + +ENTRYPOINT ["simulator", "-b=1", "-v=1", "-s=100ms", "--database=/data", "--globals={\"executorVersion\": \"v2vandenberg\"}"] +CMD ["-a=0.0.0.0"] \ No newline at end of file diff --git a/tools/cmd/simulator/main.go b/tools/cmd/simulator/main.go index 21fd366d1..b55fdb0d7 100644 --- a/tools/cmd/simulator/main.go +++ b/tools/cmd/simulator/main.go @@ -12,8 +12,12 @@ import ( "encoding/json" "fmt" "io" + "log" + "math/big" + "net" "net/http" "os" + "strings" "time" "github.com/AccumulateNetwork/jsonrpc2/v15" @@ -43,9 +47,11 @@ var flag = struct { LogFormat string Step string Globals string + BaseAddr string BvnCount int ValCount int BasePort int + Cors []string }{} func init() { @@ -59,10 +65,28 @@ func init() { cmd.Flags().IntVarP(&flag.BvnCount, "bvns", "b", 3, "Number of BVNs to create; applicable only when --network=simple") cmd.Flags().IntVarP(&flag.ValCount, "validators", "v", 3, "Number of validators to create per BVN; applicable only when --network=simple") cmd.Flags().IntVarP(&flag.BasePort, "port", "p", 26656, "Base port to listen on") + cmd.Flags().StringVarP(&flag.BaseAddr, "address", "a", "127.0.1.1", "Base address to listen on") + cmd.Flags().StringSliceVarP(&flag.Cors, "cors", "c", []string{"*"}, "Specify url's for CORS requests, (default=*)") cmd.MarkFlagsMutuallyExclusive("snapshot", "globals") } +func findLoopback() (ret []net.IP) { + ips, err := net.LookupIP("localhost") + if err != nil { + log.Fatalf("Could not resolve localhost: %v\n", err) + } + + // Find and print the default 127.x.x.x address (typically 127.0.0.1) + for i, ip := range ips { + if ip.To4() != nil && ip.IsLoopback() { + fmt.Printf("Default 127.x.x.x address: %s\n", ip.String()) + ret = append(ret, ips[i]) + } + } + return ret +} + var DefaultLogLevels = config.LogLevel{}. Parse(config.DefaultLogLevels). SetModule("sim", "info"). @@ -71,7 +95,35 @@ var DefaultLogLevels = config.LogLevel{}. func main() { _ = cmd.Execute() } +func nextIP(ip net.IP, addToIP int) net.IP { + // Convert IP to a big.Int + ipInt := big.NewInt(0).SetBytes(ip.To16()) // To16 ensures it works for both IPv4 and IPv6 + + // Add 1 to the IP address + ipInt.Add(ipInt, big.NewInt(int64(addToIP))) + + // Convert back to IP + newIP := ipInt.Bytes() + + // Handle IPv4 by slicing the last 4 bytes + if ip.To4() != nil { + return net.IP(newIP[len(newIP)-4:]) + } + return net.IP(newIP) +} + func run(*cobra.Command, []string) { + var baseAddr net.IP + if flag.BaseAddr == "localhost" { + ips := findLoopback() + if len(ips) == 0 { + log.Fatal("No IP addresses found") + } + baseAddr = ips[0] + } else { + baseAddr = net.ParseIP(flag.BaseAddr) + } + jsonrpc2.DebugMethodFunc = true var opts []simulator.Option @@ -84,7 +136,7 @@ func run(*cobra.Command, []string) { net = simulator.NewSimpleNetwork("Simulator", flag.BvnCount, flag.ValCount) for i, bvn := range net.Bvns { for j, node := range bvn.Nodes { - node.AdvertizeAddress = fmt.Sprintf("127.0.1.%d", 1+i*flag.ValCount+j) + node.AdvertizeAddress = nextIP(baseAddr, i*flag.ValCount+j).String() node.BasePort = uint64(flag.BasePort) } } @@ -128,6 +180,9 @@ func run(*cobra.Command, []string) { ListenHTTPv3: true, ServeError: check, HookHTTP: func(h http.Handler, w http.ResponseWriter, r *http.Request) { + if handleCORS(w, r) { + return + } onWaitHook(sim, h, w, r) }, })) @@ -149,6 +204,12 @@ func run(*cobra.Command, []string) { ListenHTTPv2: true, ListenHTTPv3: true, ServeError: check, + HookHTTP: func(h http.Handler, w http.ResponseWriter, r *http.Request) { + if handleCORS(w, r) { + return + } + h.ServeHTTP(w, r) + }, })) select {} @@ -250,3 +311,21 @@ func waitForTxID(sim *simulator.Simulator, txid *url.TxID, ignorePending bool) { } } } + +func handleCORS(w http.ResponseWriter, r *http.Request) bool { + if flag.Cors == nil { + return false + } + + cors := strings.Join(flag.Cors, ",") + w.Header().Set("Access-Control-Allow-Origin", cors) // set "*" for testing, but use specific origin in production + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + + // Handle preflight OPTIONS request + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return true + } + return false +}