Fix transaction hash search for Sui (#448)

### Description

Tracking issue: https://github.com/wormhole-foundation/wormhole-explorer/issues/447

For Sui transaction hashes, calling `GET /api/v1/vaas?txHash={hash}` returned an HTTP status code of 400 and a message of `"MALFORMED TX HASH"`.

This pull request fixes the problem.
This commit is contained in:
agodnic 2023-06-22 10:32:17 -03:00 committed by GitHub
parent 317e411900
commit 8b58196181
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 14 deletions

View File

@ -10,6 +10,8 @@ import (
)
const (
suiMinTxHashLen = 43
suiMaxTxHashLen = 44
algorandTxHashLen = 52
wormholeMinTxHashLen = 64
wormholeMaxTxHashLen = 66
@ -39,11 +41,16 @@ func ParseTxHash(value string) (*TxHash, error) {
return parseSolanaTxHash(value)
}
// Algorand txHashes are 52 bytes long, encoded as base32.
// Algorand txHashes are 32 bytes long, encoded as base32.
if len(value) == algorandTxHashLen {
return parseAlgorandTxHash(value)
}
// Sui txHashes are 32 bytes long, encoded as base32.
if len(value) >= suiMinTxHashLen && len(value) <= suiMaxTxHashLen {
return parseSuiTxHash(value)
}
// Wormhole txHashes are 32 bytes long, encoded as hex.
// Optionally, they can be prefixed with "0x" or "0X".
if len(value) >= wormholeMinTxHashLen && len(value) <= wormholeMaxTxHashLen {
@ -58,7 +65,7 @@ func parseSolanaTxHash(value string) (*TxHash, error) {
// Decode the string from base58 to binary
bytes, err := base58.Decode(value)
if err != nil {
return nil, fmt.Errorf("failed to decode txHash from base58: %w", err)
return nil, fmt.Errorf("failed to decode solana txHash from base58: %w", err)
}
// Make sure we have the expected amount of bytes
@ -78,7 +85,7 @@ func parseAlgorandTxHash(value string) (*TxHash, error) {
// Decode the string from base32 to binary
bytes, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(value)
if err != nil {
return nil, fmt.Errorf("failed to decode txHash from base32: %w", err)
return nil, fmt.Errorf("failed to decode algorand txHash from base32: %w", err)
}
// Make sure we have the expected amount of bytes
@ -94,6 +101,27 @@ func parseAlgorandTxHash(value string) (*TxHash, error) {
return &result, nil
}
func parseSuiTxHash(value string) (*TxHash, error) {
// Decode the string from base58 to binary
bytes, err := base58.Decode(value)
if err != nil {
return nil, fmt.Errorf("failed to decode sui txHash from base58: %w", err)
}
// Make sure we have the expected amount of bytes
if len(bytes) != 32 {
return nil, fmt.Errorf("sui txHash must be exactly 32 bytes, but got %d bytes", len(bytes))
}
// Populate the result struct and return
result := TxHash{
hash: base58.Encode(bytes),
isWormhole: true,
}
return &result, nil
}
func parseWormholeTxHash(value string) (*TxHash, error) {
// Trim any preceding "0x" to the address

View File

@ -13,24 +13,48 @@ func TestParseTxHash(t *testing.T) {
isWormholeTxHash bool
}{
{
// Solana hash - 88 characters
// Invalid Solana hash - 86 characters (too short)
input: "VKrJx5ak3amnpY5EXiqfu6pnrzxHTLU95m9vfbYnGSSLQrkRzb4tm4NztCGeLcJxieXQYnqddUwoaEsDRTRh57",
},
{
// Valid Solana hash - 88 characters
input: "2maR6uDZzroV7JFF76rp5QR4CFP1PFUe76VRE8gF8QtWRifpGAKJQo4SQDBNs3TAM9RrchJhnJ644jUL2yfagZco",
output: "2maR6uDZzroV7JFF76rp5QR4CFP1PFUe76VRE8gF8QtWRifpGAKJQo4SQDBNs3TAM9RrchJhnJ644jUL2yfagZco",
isSolanaTxHash: true,
},
{
// Solana hash - 87 characters
// Valid Solana hash - 87 characters
input: "VKrJx5ak3amnpY5EXiqfu6pnrzxHTLU95m9vfbYnGSSLQrkRzb4tm4NztCGeLcJxieXQYnqddUwoaEsDRTRh57R",
output: "VKrJx5ak3amnpY5EXiqfu6pnrzxHTLU95m9vfbYnGSSLQrkRzb4tm4NztCGeLcJxieXQYnqddUwoaEsDRTRh57R",
isSolanaTxHash: true,
},
{
// Solana hash w/ invalid length (86 characters)
input: "VKrJx5ak3amnpY5EXiqfu6pnrzxHTLU95m9vfbYnGSSLQrkRzb4tm4NztCGeLcJxieXQYnqddUwoaEsDRTRh57",
// Invalid Solana hash - 89 characters (too long)
input: "2maR6uDZzroV7JFF76rp5QR4CFP1PFUe76VRE8gF8QtWRifpGAKJQo4SQDBNs3TAM9RrchJhnJ644jUL2yfagZco2",
},
{
// Solana hash w/ invalid length (89 characters)
input: "2maR6uDZzroV7JFF76rp5QR4CFP1PFUe76VRE8gF8QtWRifpGAKJQo4SQDBNs3TAM9RrchJhnJ644jUL2yfagZco2",
// Invalid Sui hash - 42 characters (too short)
input: "cVWa8xZtWbTxXQGLQaYquwmChE2sQYxFNGnHmp6oXX",
},
{
// Valid Sui hash - 43 characters
input: "cVWa8xZtWbTxXQGLQaYquwmChE2sQYxFNGnHmp6oXXL",
output: "cVWa8xZtWbTxXQGLQaYquwmChE2sQYxFNGnHmp6oXXL",
isWormholeTxHash: true,
},
{
// Valid Sui hash - 44 characters
input: "9yQWLTNmFkwZ6CdK3QXhk8utKr42n3Eh1CFFBWcdCeJC",
output: "9yQWLTNmFkwZ6CdK3QXhk8utKr42n3Eh1CFFBWcdCeJC",
isWormholeTxHash: true,
},
{
// Invalid Sui hash - 45 characters (too long)
input: "9yQWLTNmFkwZ6CdK3QXhk8utKr42n3Eh1CFFBWcdCeJC9",
},
{
// Invalid Wormhole hash - 63 characters (too short)
input: "f77f8b44f35ff047a74ee8235ce007afbab357d4e30010d51b6f6990f921637",
},
{
// Wormhole hash with 0x prefix
@ -51,14 +75,12 @@ func TestParseTxHash(t *testing.T) {
isWormholeTxHash: true,
},
{
// Wormhole hash w/ indalid length
input: "33f77f8b44f35ff047a74ee8235ce007afbab357d4e30010d51b6f6990f921637",
output: "",
// Invalid Wormhole hash - 65 characters (too long)
input: "33f77f8b44f35ff047a74ee8235ce007afbab357d4e30010d51b6f6990f921637",
},
{
// A bunch of random characters
input: "434234i32042oiu08d8sauf0suif",
output: "",
input: "434234i32042oiu08d8sauf0suif",
},
}