Skip to content

Commit

Permalink
Merge pull request #1 from keep-starknet-strange/dev/ci-and-full-inte…
Browse files Browse the repository at this point in the history
…gration

dev: Local integration setup
  • Loading branch information
b-j-roberts authored Mar 28, 2024
2 parents d0ecb9e + cbe3065 commit 1238bcf
Show file tree
Hide file tree
Showing 23 changed files with 965 additions and 1,637 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ jobs:
- uses: actions/checkout@v4
- uses: asdf-vm/actions/install@v3
- run: scarb fmt --check
working-directory: onchain
- run: scarb build
working-directory: onchain
1 change: 1 addition & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ jobs:
- uses: actions/checkout@v4
- uses: asdf-vm/actions/install@v3
- run: snforge test
working-directory: onchain
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
./yarn.lock
node_modules/

# Redis
**/dump.rdb

# Development
**/TODO

# TODO
# Frontend
# Backend
File renamed without changes.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<div align="center">
<!--TODO: <img src="book/src/assets/blobstreamSnBanner.png" alt="art_canvas" height="300"/>-->
<h1>art/peace</h1>
<img src="resources/art-peace-logo/art-peace.jpg" alt="art_canvas" height="300"/>

***Collaborative art canvas on Starknet***

Expand Down
223 changes: 202 additions & 21 deletions backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,141 @@ package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os/exec"
"strconv"

"github.com/gorilla/websocket"
"github.com/redis/go-redis/v9"
)
var client *redis.Client
// Vector of connections
var connections []*websocket.Conn

// Message layout
/*
{
"data": {
"cursor": {
"orderKey": 3,
"uniqueKey": "0x050d47ba775556cd86577692d31a38422af66471dcb85edaea33cde70bfc156c"
},
"end_cursor": {
"orderKey": 4,
"uniqueKey": "0x03b2711fe29eba45f2a0250c34901d15e37b495599fac1a74960a09cc83e1234"
},
"finality": "DATA_STATUS_ACCEPTED",
"batch": [
{
"status": "BLOCK_STATUS_ACCEPTED_ON_L2",
"events": [
{
"event": {
"fromAddress": "0x0474642f7f488d4b49b6e892f3e4a5407c6ad5fe065687f2ebe4e0f7c1309860",
"keys": [
"0x02d7b50ebf415606d77c7e7842546fc13f8acfbfd16f7bcf2bc2d08f54114c23",
"0x0328ced46664355fc4b885ae7011af202313056a7e3d44827fb24c9d3206aaa0",
"0x000000000000000000000000000000000000000000000000000000000000001e"
],
"data": [
"0x0000000000000000000000000000000000000000000000000000000000000001"
]
}
}
]
}
]
}
}
*/

func consumeIndexerMsg(w http.ResponseWriter, r *http.Request) {
log.Println("Consume indexer msg")
fmt.Println("Consume indexer msg")

requestBody, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Error reading request body: ", err)
fmt.Println("Error reading request body: ", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

fmt.Println("Received message: ", string(requestBody))

// TODO: Parse message fully, check block status, number, ...
reqBody := map[string]interface{}{}
err = json.Unmarshal(requestBody, &reqBody)
if err != nil {
fmt.Println("Error unmarshalling request body: ", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
// Get the field data.batch[0].events[0].event.keys[2]
posHex := reqBody["data"].(map[string]interface{})["batch"].([]interface{})[0].(map[string]interface{})["events"].([]interface{})[0].(map[string]interface{})["event"].(map[string]interface{})["keys"].([]interface{})[2]

// Get the field data.batch[0].events[0].event.data[0]
colorHex := reqBody["data"].(map[string]interface{})["batch"].([]interface{})[0].(map[string]interface{})["events"].([]interface{})[0].(map[string]interface{})["event"].(map[string]interface{})["data"].([]interface{})[0]

log.Println("Received message: ", string(requestBody))
// Convert hex to int
position, err := strconv.ParseInt(posHex.(string), 0, 64)
if err != nil {
fmt.Println("Error converting position hex to int: ", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
color, err := strconv.ParseInt(colorHex.(string), 0, 64)
if err != nil {
fmt.Println("Error converting color hex to int: ", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

colorBitWidth := uint(5) // TODO: Get from request || const / cmdline?
bitfieldType := "u" + strconv.Itoa(int(colorBitWidth))
pos := uint(position) * colorBitWidth

ctx := context.Background()
err = client.BitField(ctx, "canvas", "SET", bitfieldType, pos, color).Err()
if err != nil {
panic(err)
}

var message = map[string]interface{}{
"position": position,
"color": color,
}
messageBytes, err := json.Marshal(message)
if err != nil {
fmt.Println("Error marshalling message: ", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
fmt.Println("Message: ", string(messageBytes), " to clients: ", len(connections))
// Send message to all connected clients
for idx, conn := range connections {
if err := conn.WriteMessage(websocket.TextMessage, messageBytes); err != nil {
fmt.Println(err)
// Remove connection
conn.Close()
connections = append(connections[:idx], connections[idx+1:]...)
}
}
}

func initCanvas(w http.ResponseWriter, r *http.Request) {
log.Println("Initializing Canvas...")
fmt.Println("Initializing Canvas...")

// TODO: Check if canvas already exists

reqBody, err := io.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
panic(err)
}
var jsonBody map[string]uint
err = json.Unmarshal(reqBody, &jsonBody)
if err != nil {
log.Fatal(err)
panic(err)
}
// TODO: Check if width and height are valid
width := jsonBody["width"]
Expand All @@ -53,35 +153,37 @@ func initCanvas(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
err = client.Set(ctx, "canvas", canvas, 0).Err()
if err != nil {
log.Fatal(err)
panic(err)
}

log.Println("Canvas initialized")
fmt.Println("Canvas initialized")
}

func getCanvas(w http.ResponseWriter, r *http.Request) {
log.Println("Get Canvas")
fmt.Println("Get Canvas")

ctx := context.Background()
val, err := client.Get(ctx, "canvas").Result()
if err != nil {
log.Fatal(err)
panic(err)
}

log.Println("Canvas", val)
fmt.Println("Canvas", val)
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Write([]byte(val))
}

func placePixel(w http.ResponseWriter, r *http.Request) {
log.Println("Place Pixel")
fmt.Println("Place Pixel")

reqBody, err := io.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
panic(err)
}
var jsonBody map[string]uint
err = json.Unmarshal(reqBody, &jsonBody)
if err != nil {
log.Fatal(err)
panic(err)
}
// TODO: Check if pos and color are valid
// TODO: allow x, y coordinates?
Expand All @@ -94,16 +196,16 @@ func placePixel(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
err = client.BitField(ctx, "canvas", "SET", bitfieldType, pos, color).Err()
if err != nil {
log.Fatal(err)
panic(err)
}
}

func getPixel(w http.ResponseWriter, r *http.Request) {
log.Println("Get Pixel")
fmt.Println("Get Pixel")

position, err := strconv.Atoi(r.URL.Query().Get("position"))
if err != nil {
log.Fatal(err)
panic(err)
}
colorBitWidth := uint(5) // TODO: Get from request || const / cmdline?
bitfieldType := "u" + strconv.Itoa(int(colorBitWidth))
Expand All @@ -112,10 +214,87 @@ func getPixel(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
val, err := client.BitField(ctx, "canvas", "GET", bitfieldType, pos).Result()
if err != nil {
log.Fatal(err)
panic(err)
}

fmt.Println("Pixel", val)
}

func placePixelDevnet(w http.ResponseWriter, r *http.Request) {
fmt.Println("Place Pixel")

reqBody, err := io.ReadAll(r.Body)
if err != nil {
panic(err)
}
fmt.Println(reqBody)
var jsonBody map[string]string
err = json.Unmarshal(reqBody, &jsonBody)
if err != nil {
panic(err)
}
fmt.Println(jsonBody)

x, err := strconv.Atoi(jsonBody["x"])
if err != nil {
panic(err)
}
y, err := strconv.Atoi(jsonBody["y"])
if err != nil {
panic(err)
}
// Use shell / bash to ls files in directory
shellCmd := "../tests/integration/local/place_pixel.sh"
position := x + y * 16 // TODO: Hardcoded for now
fmt.Println("Running shell command: ", shellCmd, jsonBody["contract"], "place_pixel", strconv.Itoa(int(position)), jsonBody["color"])
cmd := exec.Command(shellCmd, jsonBody["contract"], "place_pixel", strconv.Itoa(int(position)), jsonBody["color"])
out, err := cmd.Output()
if err != nil {
fmt.Println("Error executing shell command: ", err)
panic(err)
}
fmt.Println(string(out))

w.Header().Set("Access-Control-Allow-Origin", "*")
w.Write([]byte("Pixel placed"))
}

var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}

func wsReader(conn *websocket.Conn) {
for {
fmt.Println("Reading message")
messageType, p, err := conn.ReadMessage()
if err != nil {
fmt.Println(err)
return
}
fmt.Println("read", string(p), "messageType", messageType)

//if err := conn.WriteMessage(messageType, p); err != nil {
// fmt.Println(err)
// return
//}
//fmt.Println("sent", string(p))
}
}

func wsEndpoint(w http.ResponseWriter, r *http.Request) {
fmt.Println("Websocket endpoint")
upgrader.CheckOrigin = func(r *http.Request) bool { return true }

ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println(err)
}

log.Println("Pixel", val)
fmt.Println("Client Connected")
// TODO: disconnecting / removing connections
connections = append(connections, ws)
wsReader(ws)
}

func main() {
Expand All @@ -132,8 +311,10 @@ func main() {
http.HandleFunc("/getCanvas", getCanvas)
http.HandleFunc("/placePixel", placePixel)
http.HandleFunc("/getPixel", getPixel)
http.HandleFunc("/placePixelDevnet", placePixelDevnet)
http.HandleFunc("/ws", wsEndpoint)

// TODO: hardcode port
log.Println("Listening on port 8080")
fmt.Println("Listening on port 8080")
http.ListenAndServe(":8080", nil)
}
2 changes: 2 additions & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ require github.com/redis/go-redis/v9 v9.5.1
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gorilla/websocket v1.5.1 // indirect
golang.org/x/net v0.17.0 // indirect
)
4 changes: 4 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
Loading

0 comments on commit 1238bcf

Please sign in to comment.