From 29f7900c7d93188959729bb4f3ae1aa35cc15a96 Mon Sep 17 00:00:00 2001 From: agodnic Date: Tue, 21 Mar 2023 16:12:23 -0300 Subject: [PATCH] [API] Accept emitter addresses in Solana format (#200) ### Summary This PR enables passing emitter addresses in Solana format when the `chainId` parameter is set to 1. If `chainId` is set to a different value, the API will expect a regular Wormhole address. Tracking issue: https://github.com/wormhole-foundation/wormhole-explorer/issues/189 ### Endpoints affected - `GET /api/v1/global-tx/{chain_id}/{emitter}/{seq}` - `GET /api/v1/observations/:chain/:emitter` - `GET /api/v1/observations/:chain/:emitter/:sequence` - `GET /api/v1/observations/:chain/:emitter/:sequence/:signer/:hash` - `GET /api/v1/vaas/{chain_id}/{emitter}` - `GET /api/v1/vaas/{chain_id}/{emitter}/{seq}` ### Example These two calls should be equivalent: - `GET /api/v1/vaas/1/ec7372995d5cc8732397fb0ad35c0121e0eaa90d26f828a534cab54391b3a4f5` (address in Wormhole format) - `GET /api/v1/vaas/1/Gv1KWf8DT1jKv5pKBmGaTmVszqa56Xn8YGx2Pg7i7qAk` (the same address, in Solana format) --- api/middleware/extract_parameters.go | 60 ++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/api/middleware/extract_parameters.go b/api/middleware/extract_parameters.go index 12630394..1bbe34ec 100644 --- a/api/middleware/extract_parameters.go +++ b/api/middleware/extract_parameters.go @@ -8,16 +8,17 @@ import ( "strings" "time" + solana "github.com/gagliardetto/solana-go" "github.com/gofiber/fiber/v2" "github.com/pkg/errors" "github.com/wormhole-foundation/wormhole-explorer/api/response" "github.com/wormhole-foundation/wormhole-explorer/api/types" - "github.com/wormhole-foundation/wormhole/sdk/vaa" + sdk "github.com/wormhole-foundation/wormhole/sdk/vaa" "go.uber.org/zap" ) // ExtractChainID get chain parameter from route path. -func ExtractChainID(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, error) { +func ExtractChainID(c *fiber.Ctx, l *zap.Logger) (sdk.ChainID, error) { chain, err := c.ParamsInt("chain") if err != nil { @@ -28,21 +29,46 @@ func ExtractChainID(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, error) { zap.String("requestID", requestID), ) - return vaa.ChainIDUnset, response.NewInvalidParamError(c, "WRONG CHAIN ID", errors.WithStack(err)) + return sdk.ChainIDUnset, response.NewInvalidParamError(c, "WRONG CHAIN ID", errors.WithStack(err)) } - return vaa.ChainID(chain), nil + return sdk.ChainID(chain), nil } -// ExtractEmitterAddr get emitter parameter from route path. -func ExtractEmitterAddr(c *fiber.Ctx, l *zap.Logger) (*types.Address, error) { +// ExtractEmitterAddr parses the emitter address from the request path. +// +// When the parameter `chainIdHint` is not nil, this function will attempt to parse the +// native address format of the specified chain. +// +// The fallback behavior is to parse the address according to the Wormhole hex format. +func ExtractEmitterAddr(c *fiber.Ctx, l *zap.Logger, chainIdHint *sdk.ChainID) (*types.Address, error) { emitterStr := c.Params("emitter") + // If the chain ID is Solana, attempt to parse the emitter as a Solana address. + if chainIdHint != nil && *chainIdHint == sdk.ChainIDSolana { + + // If the address fails to parse, just fall back to the Wormhole format. + sig, err := solana.PublicKeyFromBase58(emitterStr) + if err == nil { + // This step is not expected to fail, since Solana and Wormhole addresses have the same size. + // However, if it does, we log the error. + emitter, err := types.BytesToAddress(sig[:]) + if err == nil { + return emitter, nil + } + l.Warn("failed to convert Solana address to Wormhole address", + zap.String("emitterAddress", emitterStr), + zap.Error(err), + ) + } + } + + // Attempt to parse the address according to the Wormhole hex format. emitter, err := types.StringToAddress(emitterStr) if err != nil { requestID := fmt.Sprintf("%v", c.Locals("requestid")) - l.Error("failed to convert emitter to address", + l.Error("failed to convert emitter to wormhole address", zap.Error(err), zap.String("emitterStr", emitterStr), zap.String("requestID", requestID), @@ -96,14 +122,14 @@ func ExtractGuardianAddress(c *fiber.Ctx, l *zap.Logger) (*types.Address, error) } // ExtractVAAParams get VAA chain, address from route path. -func ExtractVAAChainIDEmitter(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, *types.Address, error) { +func ExtractVAAChainIDEmitter(c *fiber.Ctx, l *zap.Logger) (sdk.ChainID, *types.Address, error) { chainID, err := ExtractChainID(c, l) if err != nil { - return vaa.ChainIDUnset, nil, err + return sdk.ChainIDUnset, nil, err } - address, err := ExtractEmitterAddr(c, l) + address, err := ExtractEmitterAddr(c, l, &chainID) if err != nil { return chainID, nil, err } @@ -112,14 +138,14 @@ func ExtractVAAChainIDEmitter(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, *types. } // ExtractVAAParams get VAAA chain, address and sequence from route path. -func ExtractVAAParams(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, *types.Address, uint64, error) { +func ExtractVAAParams(c *fiber.Ctx, l *zap.Logger) (sdk.ChainID, *types.Address, uint64, error) { chainID, err := ExtractChainID(c, l) if err != nil { - return vaa.ChainIDUnset, nil, 0, err + return sdk.ChainIDUnset, nil, 0, err } - address, err := ExtractEmitterAddr(c, l) + address, err := ExtractEmitterAddr(c, l, &chainID) if err != nil { return chainID, nil, 0, err } @@ -133,11 +159,11 @@ func ExtractVAAParams(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, *types.Address, } // ExtractObservationSigner get signer from route path. -func ExtractObservationSigner(c *fiber.Ctx, l *zap.Logger) (*vaa.Address, error) { +func ExtractObservationSigner(c *fiber.Ctx, l *zap.Logger) (*sdk.Address, error) { signer := c.Params("signer") - signerAddr, err := vaa.StringToAddress(signer) + signerAddr, err := sdk.StringToAddress(signer) if err != nil { requestID := fmt.Sprintf("%v", c.Locals("requestid")) l.Error("failed to covert signer to address", @@ -163,14 +189,14 @@ func ExtractObservationHash(c *fiber.Ctx, l *zap.Logger) (string, error) { } // GetTxHash get txHash parameter from query param. -func GetTxHash(c *fiber.Ctx, l *zap.Logger) (*vaa.Address, error) { +func GetTxHash(c *fiber.Ctx, l *zap.Logger) (*sdk.Address, error) { txHash := c.Query("txHash") if txHash == "" { return nil, nil } - txHashAddr, err := vaa.StringToAddress(txHash) + txHashAddr, err := sdk.StringToAddress(txHash) if err != nil { requestID := fmt.Sprintf("%v", c.Locals("requestid")) l.Error("failed to covert txHash to address",