Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d72a6d6
Allow coordinator to be set manually
mpetrun5 Jan 30, 2025
5c1de0a
Add manual coordinator test
mpetrun5 Feb 3, 2025
de6b2ef
Implement across message handler
mpetrun5 Feb 11, 2025
913ebd0
Send signature to all relayers
mpetrun5 Feb 11, 2025
21cbcfe
Implement signature cache to store generated signatures
mpetrun5 Feb 11, 2025
6eb268c
Speed up process coordination
mpetrun5 Feb 11, 2025
2ca060c
Implement signature cache to store generated signatures
mpetrun5 Feb 12, 2025
4d80bbd
Add signature cache tests
mpetrun5 Feb 12, 2025
084d5e3
Allow for coordinator to notify other relayers of an across message
mpetrun5 Feb 12, 2025
5205828
Reduce tss timeout
mpetrun5 Feb 12, 2025
54b76d6
Implement calculation of the unlock hash that matches on-chain implem…
mpetrun5 Feb 18, 2025
89733ba
Add across message handler tests
mpetrun5 Feb 24, 2025
bf72ba3
Merge branch 'main' into feat/across-message-handler
mpetrun5 Feb 24, 2025
d38887e
Update mocks to maintained version
mpetrun5 Feb 24, 2025
9cc32f0
Fix test race condition
mpetrun5 Feb 24, 2025
9627a80
Convert abi to constant
mpetrun5 Feb 25, 2025
a6f6b10
Use source chain id property from across data
mpetrun5 Feb 25, 2025
183b8fe
Use message destination as across deposit source
mpetrun5 Feb 25, 2025
692a7f8
Use coordinator from the host in message handler
mpetrun5 Feb 25, 2025
0dfaff1
Send across message result over the err chanel
mpetrun5 Feb 25, 2025
94dedf0
Return api errors as json
mpetrun5 Feb 25, 2025
ed8e2bb
Implement api serve function
mpetrun5 Feb 25, 2025
21582cd
Add signing handler tests
mpetrun5 Feb 25, 2025
6ff8c0c
Merge branch 'main' into feat/signing-api
mpetrun5 Feb 26, 2025
6e22f4e
Ignore valid json encode errors
mpetrun5 Feb 26, 2025
290cc5a
Remove print
mpetrun5 Mar 4, 2025
bb235a8
Bump cache action
mpetrun5 Mar 4, 2025
f3640f1
Use a custom big.Int type that decode big int from string
mpetrun5 Mar 5, 2025
172ec9e
Add read timeout
mpetrun5 Mar 5, 2025
f51b347
Add custom lint configuration
mpetrun5 Mar 5, 2025
ccd5811
Remove messy linters
mpetrun5 Mar 5, 2025
5b72bdb
Lint
mpetrun5 Mar 5, 2025
6356bc4
Remove extra nolint
mpetrun5 Mar 5, 2025
8e726ae
Remove linter errors
mpetrun5 Mar 5, 2025
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
78 changes: 78 additions & 0 deletions api/handlers/signing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package handlers

import (
"encoding/json"
"fmt"
"math/big"
"net/http"

across "github.com/sprintertech/sprinter-signing/chains/evm/message"
"github.com/sygmaprotocol/sygma-core/relayer/message"
)

type SigningBody struct {
DepositId *big.Int `json:"depositId"`
ChainId uint64 `json:"chainId"`
}

type SigningHandler struct {
msgChan chan []*message.Message
chains map[uint64]struct{}
}

func NewSigningHandler(msgChan chan []*message.Message, chains map[uint64]struct{}) *SigningHandler {
return &SigningHandler{
msgChan: msgChan,
chains: chains,
}
}

// HandleSigning sends a message to the across message handler and returns status code 202
// if the deposit has been accepted for the signing process
func (h *SigningHandler) HandleSigning(w http.ResponseWriter, r *http.Request) {
b := &SigningBody{}
d := json.NewDecoder(r.Body)
err := d.Decode(b)
if err != nil {
JSONError(w, fmt.Sprintf("invalid request body: %s", err), http.StatusBadRequest)
return
}

err = h.validate(b)
if err != nil {
JSONError(w, fmt.Sprintf("invalid request body: %s", err), http.StatusBadRequest)
return
}

errChn := make(chan error, 1)
am := across.NewAcrossMessage(0, b.ChainId, across.AcrossData{
DepositId: b.DepositId,
ErrChn: errChn,
})
h.msgChan <- []*message.Message{am}

err = <-errChn
if err != nil {
JSONError(w, fmt.Sprintf("Singing failed: %s", err), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusAccepted)
}

func (h *SigningHandler) validate(b *SigningBody) error {
if b.DepositId == nil {
return fmt.Errorf("missing field 'depositId'")
}

if b.ChainId == 0 {
return fmt.Errorf("missing field 'chainId'")
}

_, ok := h.chains[b.ChainId]
if !ok {
return fmt.Errorf("chain '%d' not supported", b.ChainId)
}

return nil
}
153 changes: 153 additions & 0 deletions api/handlers/signing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package handlers_test

import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"net/http"
"net/http/httptest"
"testing"

"github.com/sprintertech/sprinter-signing/api/handlers"
across "github.com/sprintertech/sprinter-signing/chains/evm/message"
"github.com/stretchr/testify/suite"
"github.com/sygmaprotocol/sygma-core/relayer/message"
"go.uber.org/mock/gomock"
)

type SigningHandlerTestSuite struct {
suite.Suite

handler *handlers.SigningHandler
msgChn chan []*message.Message
}

func TestRunSigningHandlerTestSuite(t *testing.T) {
suite.Run(t, new(SigningHandlerTestSuite))
}

func (s *SigningHandlerTestSuite) SetupTest() {
ctrl := gomock.NewController(s.T())
defer ctrl.Finish()

chains := make(map[uint64]struct{})
chains[1] = struct{}{}

s.msgChn = make(chan []*message.Message, 1)
s.handler = handlers.NewSigningHandler(s.msgChn, chains)
}

func (s *SigningHandlerTestSuite) Test_HandleSigning_MissingDepositID() {
input := handlers.SigningBody{
ChainId: 1,
}
body, _ := json.Marshal(input)

req := httptest.NewRequest(http.MethodPost, "/signing", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")

recorder := httptest.NewRecorder()

go func() {
msg := <-s.msgChn
ad := msg[0].Data.(across.AcrossData)
ad.ErrChn <- fmt.Errorf("error handling message")
}()

s.handler.HandleSigning(recorder, req)

s.Equal(http.StatusBadRequest, recorder.Code)
}

func (s *SigningHandlerTestSuite) Test_HandleSigning_MissingChainID() {
input := handlers.SigningBody{
DepositId: big.NewInt(1000),
}
body, _ := json.Marshal(input)

req := httptest.NewRequest(http.MethodPost, "/signing", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")

recorder := httptest.NewRecorder()

go func() {
msg := <-s.msgChn
ad := msg[0].Data.(across.AcrossData)
ad.ErrChn <- fmt.Errorf("error handling message")
}()

s.handler.HandleSigning(recorder, req)

s.Equal(http.StatusBadRequest, recorder.Code)
}

func (s *SigningHandlerTestSuite) Test_HandleSigning_ChainNotSupported() {
input := handlers.SigningBody{
ChainId: 2,
DepositId: big.NewInt(1000),
}
body, _ := json.Marshal(input)

req := httptest.NewRequest(http.MethodPost, "/signing", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")

recorder := httptest.NewRecorder()

go func() {
msg := <-s.msgChn
ad := msg[0].Data.(across.AcrossData)
ad.ErrChn <- fmt.Errorf("error handling message")
}()

s.handler.HandleSigning(recorder, req)

s.Equal(http.StatusBadRequest, recorder.Code)
}

func (s *SigningHandlerTestSuite) Test_HandleSigning_ErrorHandlingMessage() {
input := handlers.SigningBody{
ChainId: 1,
DepositId: big.NewInt(1000),
}
body, _ := json.Marshal(input)

req := httptest.NewRequest(http.MethodPost, "/signing", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")

recorder := httptest.NewRecorder()

go func() {
msg := <-s.msgChn
ad := msg[0].Data.(across.AcrossData)
ad.ErrChn <- fmt.Errorf("error handling message")
}()

s.handler.HandleSigning(recorder, req)

s.Equal(http.StatusInternalServerError, recorder.Code)
}

func (s *SigningHandlerTestSuite) Test_HandleSigning_Success() {
input := handlers.SigningBody{
ChainId: 1,
DepositId: big.NewInt(1000),
}
body, _ := json.Marshal(input)

req := httptest.NewRequest(http.MethodPost, "/signing", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")

recorder := httptest.NewRecorder()

go func() {
msg := <-s.msgChn
ad := msg[0].Data.(across.AcrossData)
ad.ErrChn <- nil
}()

s.handler.HandleSigning(recorder, req)

fmt.Println(recorder.Body.String())
s.Equal(http.StatusAccepted, recorder.Code)
}
13 changes: 13 additions & 0 deletions api/handlers/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package handlers

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

func JSONError(w http.ResponseWriter, err interface{}, code int) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
_ = json.NewEncoder(w).Encode(err)
}
40 changes: 40 additions & 0 deletions api/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package api

import (
"context"
"net/http"
"time"

"github.com/rs/zerolog/log"
"github.com/sprintertech/sprinter-signing/api/handlers"
)

func Serve(
ctx context.Context,
addr string,
signingHandler *handlers.SigningHandler,
) {
mux := http.NewServeMux()
mux.HandleFunc("POST /signing", signingHandler.HandleSigning)

server := &http.Server{
Addr: addr,
}
go func() {
log.Info().Msgf("Starting server on %s", addr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
panic(err)
}
}()

<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

err := server.Shutdown(shutdownCtx)
if err != nil {
log.Err(err).Msgf("Error shutting down server")
} else {
log.Info().Msgf("Server shut down gracefully.")
}
}
Loading
Loading