Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API endpoint for manually relaying warp message #327

Merged
merged 62 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
315049b
Refactor application relayers
geoff-vball Jun 7, 2024
6254a6c
wip
geoff-vball Jun 7, 2024
021269f
Merge branch 'main' into gstuart/api-relay
geoff-vball Jun 7, 2024
ccd4ef5
Functions for relaying message from blockchain ID, warp message ID, a…
geoff-vball Jun 10, 2024
a6fb5cf
lint
geoff-vball Jun 10, 2024
d67e619
Fixes
geoff-vball Jun 10, 2024
96ecc68
WIP
geoff-vball Jun 11, 2024
7af931b
Merge branch 'main' into gstuart/api-relay
geoff-vball Jun 11, 2024
7703831
Add test for API
geoff-vball Jun 11, 2024
eb3e4a0
lint
geoff-vball Jun 11, 2024
d4042c6
Fix test
geoff-vball Jun 11, 2024
9081636
Fix test
geoff-vball Jun 11, 2024
2cc6522
Merge branch 'main' into gstuart/api-relay
geoff-vball Jun 12, 2024
818f650
small fixes
geoff-vball Jun 12, 2024
8325943
Add error messages
geoff-vball Jun 14, 2024
23a9b2a
Update relayer/relay_message_api_handler.go
geoff-vball Jun 14, 2024
a58649a
Merge branch 'main' into gstuart/api-relay
geoff-vball Jun 18, 2024
6dc2441
Review fixes
geoff-vball Jun 18, 2024
177c15f
Update main/main.go
geoff-vball Jun 18, 2024
cf3c779
Update main/main.go
geoff-vball Jun 18, 2024
664cd31
Update main/main.go
geoff-vball Jun 18, 2024
0e65039
Update main/main.go
geoff-vball Jun 18, 2024
fec26c6
Fix typo
geoff-vball Jun 18, 2024
56f4c38
Add txhash to SendMessage
geoff-vball Jun 19, 2024
7863281
Fix
geoff-vball Jun 20, 2024
039e5d7
Add endpoint for manual warp messages
geoff-vball Jun 20, 2024
e72a34b
lint
geoff-vball Jun 20, 2024
6322ac8
wip
geoff-vball Jun 20, 2024
6ed13b4
lint
geoff-vball Jun 20, 2024
d2b2ac5
Merge branch 'main' into gstuart/api-relay
geoff-vball Jun 25, 2024
358894b
Pass MessageCoordinator as param
geoff-vball Jun 25, 2024
3a67da1
Lint
geoff-vball Jun 25, 2024
53516b5
Fix
geoff-vball Jun 25, 2024
93e9a75
Refactor API
geoff-vball Jun 25, 2024
daac2cd
Add api folder
geoff-vball Jun 25, 2024
45657f7
Make message coordinator fields private
geoff-vball Jun 25, 2024
a6ed77a
Merge branch 'main' into gstuart/api-relay
geoff-vball Jun 26, 2024
a77d244
lint
geoff-vball Jun 26, 2024
14810a2
Remove unneeded fields
geoff-vball Jun 26, 2024
59ec4d4
Update main/main.go
geoff-vball Jun 26, 2024
214abb8
Update main/main.go
geoff-vball Jun 26, 2024
a39bdcf
Update relayer/message_coordinator.go
geoff-vball Jun 27, 2024
74fd902
Update api/relay_message.go
geoff-vball Jun 27, 2024
cdd5635
Update api/relay_message.go
geoff-vball Jun 27, 2024
f6e8e9c
Review fixes
geoff-vball Jun 27, 2024
753bf5b
Review fixes
geoff-vball Jun 27, 2024
1d823f5
fix
geoff-vball Jun 27, 2024
dba6fbe
Documentation
geoff-vball Jun 27, 2024
3ae8bf4
Fix test
geoff-vball Jun 27, 2024
7694982
Simplify API
geoff-vball Jun 27, 2024
570953c
Update api/relay_message.go
geoff-vball Jul 2, 2024
24e7694
Update README.md
geoff-vball Jul 2, 2024
abfb84e
Update api/relay_message.go
geoff-vball Jul 2, 2024
6995b64
Update README.md
geoff-vball Jul 2, 2024
713a80a
Update README.md
geoff-vball Jul 2, 2024
a68f7a4
Update README.md
geoff-vball Jul 2, 2024
4ade921
Update README.md
geoff-vball Jul 2, 2024
5468239
Review fixes
geoff-vball Jul 2, 2024
349e6c3
Fix test
geoff-vball Jul 2, 2024
343651b
review fixes
geoff-vball Jul 2, 2024
b4376e2
review fixes
geoff-vball Jul 2, 2024
f33e56f
Capitalize log messages
geoff-vball Jul 2, 2024
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
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,54 @@ The relayer consists of the following components:
<img src="resources/relayer-diagram.png?raw=true"></img>
</div>

### API

#### `/relay`
- Used to manually relay a Warp message. The body of the request must contain the following JSON:
```json
{
"blockchain-id": "<cb58-encoding of blockchain ID>",
"message-id": "<cb58-encoding of Warp message ID>",
"block-num": "<Block number that the message was sent in>"
}
```
- If successful, the endpoint will return the following JSON:
```json
{
"transaction-hash": "<Transaction hash that includes the delivered warp message>"
}
```

#### `/relay/message`
- Used to manually relay a warp message. The body of the request must contain the following JSON:
```json
{
"unsigned-message-bytes": "<Hex encoded byte array containing the unsigned warp message>",
"source-address": "<Hex encoding of address that sent the warp message>"
}
```
- If successful, the endpoint will return the following JSON:
```json
{
"transaction-hash": "<Transaction hash that includes the delivered Warp message>",
}
```

#### `/health`
- Takes no arguments. Returns a `200` status code if all Application Relayers are healthy. Returns a `503` status if any of the Application Relayers have experienced an unrecoverable error. Here is an example return body:
```json
{
"status": "down",
"details": {
"relayers-all": {
"status": "down",
"timestamp": "2024-06-01T05:06:07.685522Z",
"error": "<List of cb-58 encoded IDs for unhealthy relayers>"
}
}
}
```

## Testing

### Unit Tests
Expand Down
42 changes: 42 additions & 0 deletions api/health_check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package api

import (
"context"
"fmt"
"net/http"

"github.com/alexliesenfeld/health"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/logging"
"go.uber.org/atomic"
"go.uber.org/zap"
)

const HealthAPIPath = "/health"

func HandleHealthCheck(logger logging.Logger, relayerHealth map[ids.ID]*atomic.Bool) {
http.Handle(HealthAPIPath, healthCheckHandler(logger, relayerHealth))
}

func healthCheckHandler(logger logging.Logger, relayerHealth map[ids.ID]*atomic.Bool) http.Handler {
return health.NewHandler(health.NewChecker(
health.WithCheck(health.Check{
Name: "relayers-all",
Check: func(context.Context) error {
// Store the IDs as the cb58 encoding
var unhealthyRelayers []string
for id, health := range relayerHealth {
if !health.Load() {
unhealthyRelayers = append(unhealthyRelayers, id.String())
}
}

if len(unhealthyRelayers) > 0 {
logger.Fatal("relayers are unhealthy for blockchains", zap.Strings("blockchains", unhealthyRelayers))
return fmt.Errorf("relayers are unhealthy for blockchains %v", unhealthyRelayers)
}
return nil
},
}),
))
}
143 changes: 143 additions & 0 deletions api/relay_message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package api

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

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/awm-relayer/relayer"
"github.com/ava-labs/awm-relayer/types"
relayerTypes "github.com/ava-labs/awm-relayer/types"
"github.com/ethereum/go-ethereum/common"
"go.uber.org/zap"
)

const (
RelayAPIPath = "/relay"
RelayMessageAPIPath = RelayAPIPath + "/message"
)

type RelayMessageRequest struct {
// Required. cb58 encoding of the source blockchain ID for the message
BlockchainID string `json:"blockchain-id"`
// Required. cb58 encoding of the warp message ID
MessageID string `json:"message-id"`
// Required. Block number that the message was sent in
BlockNum uint64 `json:"block-num"`
}

type RelayMessageResponse struct {
// hex encoding of the source blockchain ID for the message
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
TransactionHash string `json:"transaction-hash"`
}

// Defines a manual warp message to be sent from the relayer through the API.
type ManualWarpMessageRequest struct {
UnsignedMessageBytes []byte `json:"unsigned-message-bytes"`
SourceAddress string `json:"source-address"`
}

func HandleRelayMessage(logger logging.Logger, messageCoordinator *relayer.MessageCoordinator) {
http.Handle(RelayAPIPath, relayAPIHandler(logger, messageCoordinator))
}

func HandleRelay(logger logging.Logger, messageCoordinator *relayer.MessageCoordinator) {
http.Handle(RelayMessageAPIPath, relayMessageAPIHandler(logger, messageCoordinator))
}

func relayMessageAPIHandler(logger logging.Logger, messageCoordinator *relayer.MessageCoordinator) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var req ManualWarpMessageRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logger.Warn("could not decode request body")
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

unsignedMessage, err := types.UnpackWarpMessage(req.UnsignedMessageBytes)
if err != nil {
logger.Warn("error unpacking warp message", zap.Error(err))
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

warpMessageInfo := &relayerTypes.WarpMessageInfo{
SourceAddress: common.HexToAddress(req.SourceAddress),
UnsignedMessage: unsignedMessage,
}

txHash, err := messageCoordinator.ProcessWarpMessage(warpMessageInfo)
if err != nil {
logger.Error("error processing message", zap.Error(err))
http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError)
return
}

resp, err := json.Marshal(
RelayMessageResponse{
TransactionHash: txHash.Hex(),
},
)
if err != nil {
logger.Error("error marshaling response", zap.Error(err))
http.Error(w, "error marshaling response: "+err.Error(), http.StatusInternalServerError)
return
}

_, err = w.Write(resp)
if err != nil {
logger.Error("error writing response", zap.Error(err))
}
})
}

func relayAPIHandler(logger logging.Logger, messageCoordinator *relayer.MessageCoordinator) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var req RelayMessageRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logger.Warn("could not decode request body")
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

blockchainID, err := ids.FromString(req.BlockchainID)
if err != nil {
logger.Warn("invalid blockchainID", zap.String("blockchainID", req.BlockchainID))
http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest)
return
}
messageID, err := ids.FromString(req.MessageID)
if err != nil {
logger.Warn("invalid messageID", zap.String("messageID", req.MessageID))
http.Error(w, "invalid messageID: "+err.Error(), http.StatusBadRequest)
return
}

txHash, err := messageCoordinator.ProcessMessageID(blockchainID, messageID, new(big.Int).SetUint64(req.BlockNum))
if err != nil {
logger.Error("error processing message", zap.Error(err))
http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError)
return
}

resp, err := json.Marshal(
RelayMessageResponse{
TransactionHash: txHash.Hex(),
},
)
if err != nil {
logger.Error("error marshalling response", zap.Error(err))
http.Error(w, "error marshalling response: "+err.Error(), http.StatusInternalServerError)
return
}

_, err = w.Write(resp)
if err != nil {
logger.Error("error writing response", zap.Error(err))
}
})
}
10 changes: 1 addition & 9 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ type Config struct {
SourceBlockchains []*SourceBlockchain `mapstructure:"source-blockchains" json:"source-blockchains"`
DestinationBlockchains []*DestinationBlockchain `mapstructure:"destination-blockchains" json:"destination-blockchains"`
ProcessMissedBlocks bool `mapstructure:"process-missed-blocks" json:"process-missed-blocks"`
ManualWarpMessages []*ManualWarpMessage `mapstructure:"manual-warp-messages" json:"manual-warp-messages"`

// convenience field to fetch a blockchain's subnet ID
blockchainIDToSubnetID map[ids.ID]ids.ID
Expand All @@ -71,7 +70,7 @@ func DisplayUsageText() {

// Validates the configuration
// Does not modify the public fields as derived from the configuration passed to the application,
// but does initialize private fields available through getters
// but does initialize private fields available through getters.
func (c *Config) Validate() error {
if len(c.SourceBlockchains) == 0 {
return errors.New("relayer not configured to relay from any subnets. A list of source subnets must be provided in the configuration file")
Expand Down Expand Up @@ -120,13 +119,6 @@ func (c *Config) Validate() error {
}
c.blockchainIDToSubnetID = blockchainIDToSubnetID

// Validate the manual warp messages
for i, msg := range c.ManualWarpMessages {
if err := msg.Validate(); err != nil {
return fmt.Errorf("invalid manual warp message at index %d: %w", i, err)
}
}

return nil
}

Expand Down
76 changes: 0 additions & 76 deletions config/manual_warp_message.go

This file was deleted.

Loading